---
audience: developers
summary: Stable catalogue of every error code the API and workers emit, with retriability
  guidance.
title: Error codes
path: reference/error-codes
status: published
---


# Error codes

Every ScaiScribe error is a structured envelope (spec §6.6):

```json
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "human- and LLM-legible description",
    "details": { },
    "request_id": "req_01KX…"
  }
}
```

`code` values are **stable across versions** — match on them, not on message text. Messages are written for both humans and LLM agents: they name the offending field/value, list valid alternatives, and carry an explicit `NOT a retriable error` marker when repeating the request cannot succeed.

## API-level codes

| Code | HTTP | Retriable? | Meaning |
|---|---|---|---|
| `VALIDATION_FAILED` | 422 | **No** | Request shape or value invalid. The message names the offending field(s) and the valid set. Fix the request; do not resend unchanged. |
| `NOT_FOUND` | 404 | **No** | The referenced resource doesn't exist *or isn't visible to your tenant* (cross-tenant resources report 404, not 403 — existence isn't leaked). |
| `UNAUTHENTICATED` | 401 | After re-auth | Missing/expired/invalid token, or a service token missing the `X-ScaiScribe-Tenant-Id` header. |
| `FORBIDDEN` | 403 | **No** | Authenticated but not allowed (missing role, builtin-theme deletion, etc.). |
| `QUOTA_EXCEEDED` | 403 | After quota reset | A tenant quota axis is exhausted. `details` carries `{axis, limit, used, increment}`. `renders_per_day` resets daily; count axes need cleanup or a limit raise. |
| `RATE_LIMITED` | 429 | Yes, with backoff | Too many requests. |
| `INTERNAL_ERROR` | 500 | Maybe | Unexpected server error. `request_id` is the support handle. |

## Job / render codes

Returned inside a job's `result_json` (and the completion webhook payload) or as the `code` of a 500 when a sync-wrapped call fails.

| Code | Retriable? | Meaning |
|---|---|---|
| `RENDER_FAILED` | Sometimes | The render worker failed. Message carries the worker-level cause below. |
| `INGEST_FAILED` | Sometimes | Ingestion failed (corrupt upload, unsupported inner structure). |
| `INSTANTIATE_FAILED` | Sometimes | Template instantiation failed. |
| `PREVIEW_FAILED` | Sometimes | Template/document preview failed. |
| `WORKER_FAILED` | Yes | The worker died mid-job (infra). Safe to retry. |

## Worker-level causes

These appear inside `RENDER_FAILED` messages. They identify the exact stage that broke:

| Code | Meaning |
|---|---|
| `TYPE_MISMATCH` | Spec type doesn't match the renderer invoked (internal routing bug — report it). |
| `INVALID_ELEMENT` | An element is structurally invalid (e.g. `heading.level` out of 1–6). |
| `UNSUPPORTED_ELEMENT` | Element type unknown to this renderer. |
| `UNSUPPORTED_IMAGE_FORMAT` | Image bytes are not a supported raster format. |
| `UNRESOLVED_ASSET` | An `asset://` ref reached the renderer unresolved (internal — report it). |
| `INVALID_CHART_DATA` | Chart data shape doesn't match the chart kind. |
| `OOXML_POSTPROCESS_ERROR` | Native chart/font injection into the OOXML zip failed. |
| `READ_ERROR` / `STYLES_READ_ERROR` / `MASTERS_READ_ERROR` | Worker I/O on spec/compiled-theme inputs failed (infra). |
| `RUNTIME_ERROR` | Uncaught renderer error — report with the `request_id`. |

## Theme + template specifics

| Situation | Code | Notes |
|---|---|---|
| Unknown theme in create/spec | `VALIDATION_FAILED` (422) | Message lists the themes visible to your tenant. Omit `theme` to get `office`. |
| Theme registration doesn't compile | `VALIDATION_FAILED` (422) | Nothing was saved — fix and re-PUT. |
| Deleting a builtin theme | `FORBIDDEN` (403) | Builtins can't be deleted; tenant themes shadowing them revert on delete. |
| Missing template variable | *warning, not error* | PPTX/XLSX render empty + `TEMPLATE_VARIABLE_MISSING` fidelity warning; DOCX errors (docx-templates is strict). Use `placeholder_schema` from the template listing to validate fills first. |
| Template expression not allowed | *warning* | `TEMPLATE_EXPRESSION_ERROR`; the expression renders empty. |

## Batch patch atomicity

`PATCH /v1/documents/{doc_id}` with multiple operations is **transactional**: operations validate and apply in order against an in-memory copy, and the new spec version is committed only when every operation succeeds. A failing operation (422) leaves the document exactly as it was — there are no half-built documents. Re-submit the whole corrected batch.

## Fidelity warnings (not errors)

Ingestion and rendering degrade gracefully: anything lossy emits a structured fidelity warning (`INGEST_*`, `NATIVE_FALLBACK`, `TEMPLATE_*`) in the `fidelity.warnings[]` array with a severity, never a silent drop. See [Ingestion + fidelity](/docs/scaiscribe/concepts/ingestion).
