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 retry5xxon 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
POSTcall) rather than re-using the failedjob_id - Use
Idempotency-Keyonly 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.