Error Reference

Errors fall into two distinct categories. Understanding which category an error belongs to determines where to catch it and how to respond.


Request-Time Errors

These are HTTP error responses returned on the POST or GET call itself, before or instead of a job_id. Catch these as HTTP exceptions in your HTTP client.

HTTP error_code Cause Action
401 invalid_api_key Missing Authorization header, wrong format, or revoked key Verify Authorization: Bearer mk_pub_... format. Check the key hasn't been revoked.
402 payment_required Account has insufficient credits Top up credits at the dashboard before resubmitting.
404 (no body) Job not found, or job belongs to a different API key Verify the job_id and that you're using the same key that created the job.
410 output_expired Output file expired (>30 days since job creation) Re-submit the job to generate a fresh file.
422 invalid_input Malformed request body Fix the field named in error_message. Common causes: prompt over 5000 chars, unrecognized design_mode or output_format, metadata_json not a valid JSON object, more than 3 images submitted.
422 image_parse_failed Unsupported image type or image exceeds 10 MB Use image/png, image/jpeg, image/webp, or image/gif. Each file must be ≤ 10 MB.
429 quota_exceeded Rate limit exceeded (20/min or 100/hr per API key) Wait at least 60 seconds before retrying. See rate-limits.md.

Error Response Shape

{
  "error_code": "invalid_input",
  "message": "prompt must be at most 5000 characters"
}

Retry Strategy for Request-Time Errors

  • 429: Back off at least 60 s, then retry
  • 5xx on POST/GET itself (rare — infrastructure errors): wait 2 s → 4 s → 8 s, up to 3 attempts
  • All other 4xx: Fix the request before retrying — retrying the same malformed request will always fail

Safe retry pattern using idempotency:

import uuid, time, httpx

def safe_post(payload: dict) -> str:
    idem_key = str(uuid.uuid4())
    headers = {
        "Authorization": "Bearer mk_pub_YOUR_KEY",
        "Idempotency-Key": idem_key,
    }
    for attempt in range(3):
        try:
            resp = httpx.post("https://api.makistry.ai/v1/text-to-cad",
                              headers=headers, json=payload, timeout=30)
            if resp.status_code == 429:
                time.sleep(60)
                continue
            resp.raise_for_status()
            return resp.json()["job_id"]
        except httpx.NetworkError:
            if attempt == 2:
                raise
            time.sleep(2 ** attempt)

Job-Level Errors

These appear as job.error_code and job.error_message when status == "failed" — received either via polling (GET /v1/jobs/{id}) or the webhook payload. The original POST already returned 202; these errors occur during async pipeline execution.

error_code Cause Action
geometry_validation_failed The pipeline's internal contract or plan gates rejected the geometry — typically because the prompt describes contradictory dimensional constraints or physically impossible geometry Simplify the prompt. Remove conflicting constraints. Break a complex part into separate jobs.
prompt_too_ambiguous The prompt lacked enough specificity to be interpreted unambiguously Add explicit dimensions, feature names, and reference faces. See prompt-image-guide.md.
code_generation_failed The LLM returned code that could not be parsed or executed after internal retries Retry once with the same prompt. If it fails again, simplify the geometry or switch to "pro" mode.
sandbox_timeout CadQuery geometry execution timed out after 5 internal retry attempts The geometry may be too complex for the chosen design_mode. Try "pro" or "assembly" mode.
export_failed The STEP/STL/GLB/3MF export failed after successful code generation Usually transient. Re-submit the job.
internal_error Unexpected server-side error Check the Makistry status page. Re-submit with exponential backoff.

Handling Job Failures

def handle_job(job: dict) -> None:
    if job["status"] == "success":
        download_file(job["download_url"])
        return

    code = job["error_code"]
    msg = job["error_message"]

    if code in ("geometry_validation_failed", "prompt_too_ambiguous"):
        # User needs to revise their prompt — don't auto-retry
        raise ValueError(f"Prompt issue ({code}): {msg}")

    if code in ("code_generation_failed", "sandbox_timeout", "export_failed"):
        # Potentially transient — retry once
        retry_job(job)
        return

    # internal_error: retry with backoff
    retry_job(job, backoff=True)

Retry Strategy for Job-Level Errors

  • Re-submit a new job (new POST call) rather than re-using the failed job_id
  • Use Idempotency-Key only for retrying network failures on the original POST — not for retrying a failed job
  • For internal_error: wait 30 s, then re-submit; if it fails again, wait 5 min

Idempotency

Use Idempotency-Key: <uuid> on POST requests to safely retry network failures without double-charging or creating duplicate jobs.

Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Important edge case: The same idempotency key always returns the original job_id, regardless of the request body. If you change the prompt and reuse the same key, you'll get the original job back — not a new one. Generate a fresh UUID for each logically distinct request.