API error codes and how to fix them
When a thirds.ai request fails, you get an HTTP status and a named error.code, such as insufficient_credits or template_data_invalid. Find that code in the tables on this page to see why it happened and what to do next. With thirds.ai, you save a design once, such as an invoice, a certificate, or a social post. Then you send new details and get a finished PDF or image with your logo, colours, and fonts. Your code and your AI tools get the same named codes, so one lookup works for both. You don't pay for a file that fails, so you can fix the input and try again.
Give this prompt to your AI agent. The agent does the task for you.
Read https://thirds.ai/docs/errors and help me resolve a thirds.ai error using its code and job status without exposing private content or retrying a billed action twice.
Don't want to write code? The quickstart shows how to make the same files in the editor.
What does a thirds.ai error response look like?
A thirds.ai error response is a JSON body with one error object that always holds code, message, and request_id. Your app should check code, because it names the problem and stays the same. The message is a short sentence for people, and its words can change. The request_id matches the x-request-id header, and support uses it to find your request. Here's the answer you get when a POST /v1/pdf request needs more credits than the account has:
{
"error": {
"code": "insufficient_credits",
"message": "You don't have enough credits to make this file.",
"request_id": "0b7f3c1e-5a2d-4c8e-9f61-2d4a7b9e3c10"
}
}
Some errors add one more part to the same error object:
| Part | When you see it |
|---|---|
details | A validation failure. Each entry names a field and a fixed reason. Schema failures list at most 16 entries. |
retry | A limit that clears on its own. Wait retry.retry_after_seconds before you send the request again. |
plan_limit | A plan limit, such as brand_kit_limit or batch_row_limit. It names your plan and the limit that plan sets. |
A render that fails after the request is accepted is different. The request itself succeeds, and the job shows status: "failed" with error.category and error.code. So a 200 job response can still hold a failed job. Check status before you read download. A 202 response means the job is still running. The guide to retry requests and download files shows how to poll.
Which HTTP status codes does the thirds.ai API return?
The thirds.ai API returns 200 or 202 for a render job, a 4xx status from 400 to 431 when a request can't go through as sent, and 500 or 503 when the service has a problem. Each status comes with one of the named codes on this page. This table lists the statuses that POST /v1/pdf can return, from the API reference. Other routes use the same statuses for the same kinds of problems.
| Status | What it means |
|---|---|
200 | The job has ended. Read status to see whether it succeeded, failed, or was cancelled. |
202 | The job is queued or running. Read the job again for the result. |
400 | The request failed validation, the data doesn't match the template, or the Idempotency-Key header has the wrong shape. |
401 | The request has no valid, active API key. The code is unauthorized. |
402 | The account can't pay for this file. The code is insufficient_credits, overage_limit_reached, spend_cap_reached, or overage_unavailable. |
403 | The account is suspended (account_suspended). A download link that has expired also returns 403 with download_expired. |
404 | The route, or an item in your account, doesn't exist. The code is not_found. |
405 | The route doesn't accept this HTTP method. The code is method_not_allowed. |
409 | The retry key belongs to a different request (idempotency_conflict), or the saved template version changed (template_version_changed). |
413 | The request body is too large. The code is request_too_large. |
415 | The request isn't JSON. The code is unsupported_media_type. |
422 | The file would stay longer than your plan allows. The code is retention_limit. |
429 | A rate or abuse limit was hit. The code is rate_limited, account_concurrency_limited, key_concurrency_limited, or abuse_limited. |
431 | The request has more than 64 headers or more than 32 KiB of header data. The code is request_headers_too_large. |
500 | An internal error happened. The code is internal_error. |
503 | The service is at capacity. The code is overloaded. Wait for Retry-After, then retry with the same key. |
Request and access errors
Request and access errors come from the request itself, such as its body, its API key, its route, or an ID it names. Fix the part the code names, then send the request again.
| Code | Cause and next step |
|---|---|
invalid_request | A field, value, or JSON body is invalid. Correct the fields in error.details. |
invalid_upload | The source image is unsupported, corrupt, or too large. Use a still PNG, JPEG, or WebP within the source-image limits. Send compact standard base64. A data URL prefix is accepted. This fails before the model. A create-time failure has no repair_code. |
request_too_large | The request body exceeds its limit. Reduce the body before you send it again. |
unsupported_media_type | The content type is wrong. Send the media type required by the route. |
request_headers_too_large | The request headers exceed their size limit. Send fewer or smaller headers. |
unauthorized | The API key is absent or invalid. Check the Bearer header and use an active key. |
account_suspended | The account cannot submit work. Contact support. |
not_found | The route or account-owned item is unavailable. Check the path, ID, and account. |
method_not_allowed | The route does not accept this HTTP method. Check the API reference. |
invalid_cursor | The history cursor is invalid. Start a new list request without a cursor. |
idempotency_conflict | A retry key already belongs to different input. Restore the original input, or use a new key for a new output. |
job_not_finished | The job is queued or running. Wait until it ends before deleting it. |
download_expired | The signed download link expires. Read the job again for a new link while its file remains available. |
key_limit_reached | The account has too many API keys. Revoke an unused key before creating one. |
batch_row_limit | The batch holds more rows than the account plan allows. Read error.plan_limit for the limit and the plan, then send fewer rows or move up a plan. |
retention_limit | retention_days is longer than your plan allows. A paid plan keeps a file for up to 365 days, and a free, pack-only, or trial account for up to 30. Send a smaller value or leave it out. See how long we keep your file. |
internal_error | An internal service step fails. Check an existing job first, then retry with the same retry key. Contact support if it continues. |
Rate and credit errors
Rate and credit errors mean the request was fine but the account can't start more work right now. Wait for Retry-After when the answer has one, or add credits with a plan or credit pack when the account has run out.
| Code | Cause and next step |
|---|---|
rate_limited | Requests arrive too fast. Wait for Retry-After and reduce the request rate. |
account_concurrency_limited | The account has too many live jobs. Wait for a job to finish. |
key_concurrency_limited | The API key has too many live jobs. Send fewer renders at once. |
abuse_limited | Too many renders fail today. Fix the input and wait for the daily allowance to reset. |
overloaded | The service has no room for this request. Wait for Retry-After, then retry with the same key. |
insufficient_credits | Available credits cannot cover the work. Add credits in billing. |
overage_limit_reached | The account reaches its monthly overage limit. Buy a pack or wait for the next calendar month. |
spend_cap_reached | The API key reaches its monthly overage cap. Raise that cap or use an approved key with room. |
overage_unavailable | Pending overage reporting pauses further overage. Buy a pack or try later. |
billing_unavailable | The billing service is unavailable. Try again later. |
already_subscribed | A subscription or completed checkout already exists. Refresh billing and manage the existing plan. |
checkout_superseded | A newer checkout replaces this one. Open checkout again from billing. |
no_billing_customer | The account has no billing customer record. Start a purchase before opening the billing portal. |
invalid_event | A billing webhook is invalid. Check the sender and signature. Don't send it as a render request. |
See rate limits for limits and retry headers. A refused render request creates no job or credit charge.
Template errors
Template errors mean a saved template, its data, or its sizes can't be used as sent. Read error.details for the field, line, or column when the answer has them.
| Code | Cause and next step |
|---|---|
template_not_found, gallery_template_not_found | The selected template or version is unavailable. Check its ID and your account. |
template_version_changed | The selected version changes before admission. Read the template and send an exact version. |
template_data_invalid | Data does not match the schema. Correct the fields in error.details. |
template_source_limit | Template source is too large. Reduce its size. |
template_data_limit | Template data is too large. Send less data. |
template_data_depth_limit | Data has too many nested levels. Use a flatter structure. |
template_data_collection_limit | Data has too many items or values. Split it into smaller outputs. |
template_syntax_error | Template syntax is invalid. Fix the reported line and column when present. |
template_missing_data | A required variable is missing. Supply that value or change the template. |
template_invalid_filter_input | A filter receives an unsupported value. Correct its input type or format. |
template_depth_limit | Template evaluation nests too deeply. Reduce nested blocks or calls. |
template_work_limit, template_timeout | Evaluation needs too much work or time. Reduce loops and data. |
template_output_limit | Evaluated HTML is too large. Reduce the content. |
template_evaluation_error | Evaluation cannot finish. Check expressions and values. |
template_schema_too_large, template_schema_complexity | The data schema is too large or complex. Simplify its rules. |
template_schema_draft_unsupported | The schema declares an unsupported draft. Use the supported schema form in the template guide. |
template_schema_invalid | A schema rule is invalid. Correct the schema before saving. |
template_schema_missing | A chosen template has no data schema. Add one, or ask for the saved form. |
template_schema_unsupported | A schema has a rule the strict or combined form can't keep. Ask for the saved form, or change that rule. |
template_sizes_too_many | A saved template or an adapt request names too many sizes. Send fewer sizes. |
template_sizes_too_large | The saved template bundle exceeds its total size limit. Reduce its sizes or their content. |
template_size_invalid_id, template_size_duplicate_id | A size id is invalid or repeats another size. Use a unique id in the allowed characters and length. |
template_size_invalid_name | A size name is empty, too long, or has a control character. Use a short plain name. |
template_size_invalid_dimensions | A size's width or height is outside the supported range. Use dimensions within the supported range. |
template_size_canvas_mismatch | A size's declared canvas does not match its width and height. Match the size's HTML canvas to its declared dimensions. |
template_size_data_overrides_too_large | A size's data overrides exceed the data size limit. Reduce that size's overrides. |
resize_timeout | Smart resize takes too long to adapt the source. Simplify the source template and try again. |
Brand and image errors
Brand and image errors mean a brand kit, logo, font, or picture can't be loaded or doesn't pass its checks. Logos and pictures must be still PNG, JPEG, or WebP files, and fonts must be WOFF2.
| Code | Cause and next step |
|---|---|
brand_kit_not_found | The kit or asset is unavailable. Check the ID and account. |
brand_kit_unavailable | The kit cannot be loaded. Try again later. |
brand_data_conflict | Render data conflicts with brand values. Remove the conflicting brand fields from your data. |
brand_kit_patch_empty | No kit changes are supplied. Send at least one supported field. |
brand_kit_colours_invalid | The colour list is invalid. Use the supported colour values and count. |
brand_kit_limit | The account keeps every kit its plan allows, archived kits too. Read error.plan_limit for the limit and the plan, then move up a plan to add one. |
brand_asset_too_large | The file is too large. Upload a smaller file. |
brand_asset_type_unsupported | The media type is unsupported. Use a still PNG, JPEG, or WebP logo, or a WOFF2 font. SVG is not accepted. |
brand_image_dimensions_invalid, brand_image_pixels_exceeded | The logo dimensions exceed the limits. Resize it. |
brand_image_animated | The logo is animated. Upload a still image. |
brand_font_tables_invalid, brand_font_glyphs_exceeded | The font exceeds validation limits. Use a valid smaller font. |
brand_asset_invalid | The file bytes are invalid or do not match media_type. Export a fresh still PNG, JPEG, or WebP. SVG is not accepted. |
brand_asset_kind_invalid, brand_asset_kind_mismatch | The asset kind is invalid or differs from the replaced file. Use the correct logo or font kind. |
brand_asset_limit_reached, brand_asset_account_limit_reached | The kit or account reaches its file allowance. Remove unused files or reduce their size. |
image_asset_too_large, image_asset_invalid | An uploaded image is too large or invalid. Use a still PNG, JPEG, or WebP within the image limits. |
image_asset_not_found | The image is missing or belongs to another account. Upload it to this account. |
image_asset_unavailable | Image storage or reading fails. Try later. |
image_asset_reference_invalid | The private image reference is invalid. Use the exact reference from the upload response. |
image_asset_header_unsupported | A private reference appears in a PDF header or footer. Move the image into the body. |
image_asset_limit | The render contains too many image bytes. Reduce the images. |
image_url_invalid | The image URL is invalid or too long. Use a public HTTP or HTTPS address. |
image_url_unavailable | The image URL could not load. Check that it serves a still PNG, JPEG, or WebP and does not redirect too many times. |
AI and editor errors
AI and editor errors come from the AI template chat and the checks the editor runs before you save. Most of them clear when you wait for the current AI work to finish or try again later.
| Code | Cause and next step |
|---|---|
provider_unavailable | The AI provider cannot complete the call. Try later. |
ai_failure_limit_reached | Too many AI operations fail. Stop repeated attempts and try later. |
operation_limit_exceeded | The account has too many pending AI operations, or an active operation reaches its budget. Wait for pending work, or reduce the request. |
message_not_found, template_draft_not_found | The chat message or its draft is unavailable. Check the template ID and the message ID, then open the template again. |
operation_conflict | Another operation conflicts with this change. Refresh the chat before retrying. |
operation_pending | An operation is still active. Wait for it to finish. |
generated_template_invalid | Generated content fails validation. Adjust the request and try again. |
render_probe_required | A required render check is missing. Run the check in the editor. |
render_probe_busy | A render check is active. Wait for it to finish. |
render_probe_not_configured, render_probe_failed, render_probe_timeout | The editor check is unavailable or fails. Try again. Contact support if it continues. |
Browser, playground, and webhook errors
Browser, playground, and webhook errors come from signing in, the free playground on the website, testimonials, and webhook settings. Your own API code rarely sees them.
| Code | Cause and next step |
|---|---|
signed_out | The browser session ends. Sign in again. |
cross_origin_rejected, csrf_rejected | The browser request fails an origin or session safety check. Reload thirds.ai and try from its page. |
invalid_email, invalid_profile | Account details are invalid. Correct the fields. |
email_already_set | The account already has an email. Refresh your account details. |
invalid_link | The sign-in link is invalid or expired. Request a new link. |
auth_unavailable | Sign-in is unavailable. Try again later. |
policy_version_stale | The policy changes before acceptance. Reload and read the current policy. |
captcha_rejected | The playground safety check fails. Complete a fresh check. |
playground_request_invalid, playground_selection_invalid | Playground input or template selection is invalid. Choose a current template and correct its fields. |
playground_daily_limited | This address made its free files for today. Sign in to use account credits. |
playground_busy, playground_unavailable | Playground capacity or service is unavailable. Try later. |
testimonial_invalid | The submitted testimonial is invalid. Correct its fields. |
testimonial_busy, testimonial_rate_limited, testimonial_unavailable | Testimonial submission is busy, limited, or unavailable. Wait before trying again. |
webhook_limit_reached | The account reaches its webhook limit. Remove an unused endpoint. |
Failed render categories
A failed render has one of six values in error.category, and each one matches a message in render history. Fix input, safety, size, and timeout failures before making a new job. Internal recovery runs before a job reaches a final failure.
| Category | Meaning and next step |
|---|---|
invalid_input | The template, HTML, or script cannot render. Fix the input. |
unsafe_asset | An asset address is not allowed. Use an allowed public address or uploaded file. |
resource_limit | The render exceeds a safety limit. Make it smaller. |
timeout | The render takes too long. Simplify it or fix script readiness. |
renderer_failure | The browser or worker fails. Try a new render with a limit on retries. |
internal_failure | An internal step fails. Try a new render. Contact support if it continues. |
Render error codes
Render error codes give the exact reason a render job failed, next to its category in error.code. Fix input, size, and script problems before you send a new render.
| Code | Cause and next step |
|---|---|
conflicting_header_footer | Header and footer options conflict. Correct the options. |
conflicting_page_size, missing_page_size | Page size settings conflict or are incomplete. Use one named format, or both width and height. |
duplicate_template | The render contract repeats a template. Send one template. |
empty_html, invalid_html, invalid_encoding | HTML is empty, invalid, or not valid UTF-8. Correct the source. |
invalid_identifier, invalid_request_id | An internal identifier is invalid. Contact support with the public request ID. |
invalid_measurement, invalid_page_format | A page length or format is invalid. Use the supported options. |
invalid_type, missing_field, unknown_field, value_out_of_range, value_too_large | A field has an invalid type, name, size, or value. Correct the request fields. |
unsupported_contract_version | Renderer contract versions do not agree. Contact support. |
unsupported_javascript_mode | The JavaScript mode is unsupported. Use the documented mode. |
javascript_action_denied | A script attempts a blocked browser action. Remove navigation, popups, frames, or workers. |
javascript_exception | A script throws an error. Fix the script before rendering. |
template_font_unavailable, template_image_unavailable | A required layout font or image cannot load. Check or replace the asset. |
template_layout_invalid | The marked layout is invalid. Correct the template layout. |
template_layout_overflow, template_text_overflow | Layout or text exceeds its bounds. Enlarge the block or reduce the content. |
asset_destination_denied, external_asset_blocked | An asset violates the fetch policy. Replace private, local, or blocked destinations with allowed assets. |
asset_count_exceeded | Too many assets are fetched. Reduce the asset count. |
asset_too_large, fetched_assets_too_large | One asset or all fetched assets exceed the byte limit. Compress or remove assets. |
input_too_large, output_too_large, too_many_pages | Input, output, or page count exceeds its limit. Split the document or reduce content. |
cpu_limit_exceeded, memory_limit_exceeded, process_limit_exceeded, temp_limit_exceeded | The render uses too many system resources. Simplify its HTML, scripts, and assets. |
dom_limit_exceeded, template_layout_limit | The document or marked layout has too many elements. Reduce its size. |
javascript_readiness_timeout | The ready marker is missing after two seconds. Set it after script work finishes. |
render_timeout | Rendering exceeds the time limit. Reduce the work. |
queue_timeout | The renderer's internal queue wait expires. Try later. |
browser_crashed, cleanup_failed, image_capture_failed, pdf_protocol_failed, render_failed | The renderer cannot finish its attempt. After final failure, try a new render with bounded retries. |
worker_busy, worker_not_ready | A worker is busy or unavailable. After final failure, try later. |
artifact_write_failed, worker_internal_failure | The worker cannot store output or complete an internal step. After final failure, retry or contact support. |
More worker and asset codes
More worker and asset codes cover brand kit text, image upload types, and steps inside the render worker. For most worker codes, the next step is a new render after the job has failed for good.
| Code | Cause and next step |
|---|---|
brand_kit_name_invalid, brand_kit_tone_invalid | Kit text is invalid. Use non-empty text within the field limit. |
image_asset_type_unsupported | The upload type is unsupported. Use PNG, JPEG, or WebP. |
renderer_contract, renderer_artifact | The renderer response or output fails verification. Retry after final failure. Contact support if it continues. |
renderer_no_answer, renderer_unavailable | The renderer gives no usable response or cannot start. Retry after final failure. |
renderer_output_stale, renderer_output_unread, artifact_not_stored | Output cleanup, reading, or storage fails. Retry after final failure. |
renderer_deadline | The worker deadline ends before the renderer returns. Simplify the render. Contact support if it continues. |
artifact_too_large | Output exceeds the storage size limit. Reduce the document or image. |
input_not_envelope, input_unavailable | Stored input is invalid or unavailable. Submit a new render. Contact support if it continues. |
input_unreadable, worker_stopping, lease_expired | Input reading fails or the worker stops or loses its job lease. After final failure, try a new render. |
Renderer diagnostic codes
Renderer diagnostic codes describe why one asset, such as a font or picture, didn't load during a render. They're separate from job error codes, and the public job response doesn't include the renderer report.
| Code | Cause and next step |
|---|---|
blocked_by_policy | Asset access is blocked. Use an allowed destination. |
http_error, dns_error, connection_error | The asset host fails to serve or resolve. Check the asset URL and host. |
timeout | An asset request times out. Use a faster or smaller asset. |
response_too_large, resource_limit | Asset loading exceeds a limit. Reduce the file size or count. |
unsupported_type, font_parse_failed | The asset format or font is invalid. Replace it with a supported file. |
unknown | No more specific diagnostic is available. Check the job code or contact support. |
AI operation failure codes
AI operation failure codes explain why an AI chat or image import ended without a result. An AI operation returns failure_category and failure_code after it ends. These are separate from HTTP request errors. A repairing or failed message can also include repair_code. That field names the last safe generation or render code. It never holds provider or customer text. MCP results can add a short repair_hint for the same code. The same idempotency key replays a terminal result. Use a new key for a new attempt.
| Code | Cause and next step |
|---|---|
provider_timeout | The AI call takes too long. Try later. |
provider_refusal | The provider declines the request. Change the request. |
provider_invalid_output | The provider JSON or template is invalid. Read repair_code for the last validation step. The same idempotency key replays this failure. Use a new key for a new attempt. |
invalid_upload, unsupported_input | The source file is unsupported or invalid. Use a supported still image or valid HTML. An invalid upload at create time fails before the model. |
invalid_output_dimensions | The requested output size is invalid. Use supported dimensions. |
brand_kit_conflict | The draft and selected brand kit conflict. Refresh the draft and kit selection. |
review_limit_reached | Automatic review used every slot. Read repair_code and any remaining draft, then send a new edit. An image import has five slots, and prompt and HTML have one. |
candidate_render_failed, final_render_failed | The AI preview or final render fails. Check the design and its assets, then send a new edit with a new idempotency key. |
operation_cancelled | The operation is no longer active. Start a new operation if you still need it. |
invalid_operation_phase, content_invalid, draft_invalid, request_invalid | Stored operation content or state is invalid. Retry. Contact support if it continues. |
database_failed, content_unavailable, draft_unavailable, brand_asset_unavailable, preview_unavailable, preview_storage_failed, preview_queue_failed, worker_crashed | An internal AI step cannot read, save, or finish its work. Retry after final failure. Contact support if it continues. |
MCP protocol errors
MCP protocol errors are numeric JSON-RPC codes that an AI tool gets when its MCP message itself is wrong. Tool errors can also carry the named API codes on this page. The guide to connect with MCP shows the setup. For Codex, the steps to connect Codex to thirds.ai show how to check the connection with codex mcp list. In Claude Code, run claude mcp get thirds to see the error, as the Claude Code MCP server guide shows. In Gemini CLI, run /mcp to see whether thirds connected, as the Gemini MCP server setup shows.
| Code | Cause and next step |
|---|---|
-32700 | JSON cannot be parsed. Send valid JSON. |
-32600 | The request or transport headers are invalid. Check your MCP client's request format. |
-32601 | The method is unknown. Use a supported MCP method. |
-32602 | Tool arguments or the tool name are invalid. Read the current tool schema. |
-32603 | Transport capacity is exhausted. Follow the retry delay in the error data. |
-32020 | Protocol headers do not match the request. Update the client headers. |
-32022 | The protocol version is unsupported. Use a version listed in the error's supported versions. |
Get help safely
To get help with an error, send support the request ID, job ID, error category, and code. Do not send API keys, signed download links, private HTML, or customer data. For an unknown code, keep the code and stop automatic retries until you know whether a job exists. If your question isn't about an error code, such as how to create a PDF from a template or how long your files stay, look in common questions.