Error Handling
Handle common Citadel errors, billing states, limits, rate limits, and async states.
Goal
Build safe fallback behavior for scan errors.
Pause high-risk workflows for review. Low-risk workflows can retry or use a limited fallback.
Error Shape
Errors may include an error message, code, request ID, and scan group ID.
{
"error": "scan_phase must be 'input' or 'output'",
"code": "invalid_scan_phase",
"request_id": "ab82f4ad-8d64-4bb4-b4ed-77df63291198",
"scan_group_id": "9b3e4f8d-96c9-4f42-8338-8cf9571c1c70"
}Status Codes
| Status | Meaning | Fix |
|---|---|---|
202 | Scan accepted and pending. | Store scan_id, poll Location, and honor Retry-After. Do not route as final. |
400 | Bad request. | Check scan_phase, content_type, UUID fields, and payload shape. |
402 | Billing, quota, spending limit, or tier cap. | Show upgrade, route to admin, or route to review. |
409 | Idempotency or duplicate request conflict. | Fetch existing result or retry with a new request_id. |
413 | Payload too large, PDF page cap, embedded image cap, or payload complexity. | Reduce size, split the file, remove excess embedded images, or review manually. |
415 | Unsupported original-file format for focus=metadata. | Upload a supported JPEG, PNG, WebP, GIF, classic TIFF, HEIF, or PDF. |
429 | Citadel is busy or the request rate is too high; no new scan was accepted. | Honor Retry-After, then retry the same request. |
503 | Citadel could not accept the scan; no scan was started. | Retry the same request after Retry-After when present, then route to review. |
500 | Server error. | Retry safely, then route to review. |
Safe Fallback Policy
export function fallbackForScanError(status: number, workflowRisk: "low" | "high") {
if (status === 400) return "fix_request";
if (status === 402) return "billing_or_tier_action";
if (status === 413) return "reduce_or_review";
if (status === 415) return "fix_request";
if (status === 429) return "retry_with_backoff";
return workflowRisk === "high" ? "manual_review" : "retry_then_degrade";
}Common 400 Causes
- Missing
scan_phase. scan_phase=outputwithoutscan_group_id.- Invalid UUID in
request_idorscan_group_id. content_typeoutsideauto,text,image,pdf, ordocument.- Invalid
focusoutsidesteg,ai,edits,all,metadata, and supported two-path combinations. Deprecated aliases are accepted only for compatibility. Structured documents accept routing focus values, but image/PDF visual evidence does not apply. focus=metadatawith text or a structured document returnsmetadata_unsupported_modality. This focus requires an original JPEG, PNG, WebP, GIF, classic TIFF, HEIF, or PDF inmode=secure.- Base64 missing when JSON contains non-text content.
- Multipart request without
fileorcontent.
Billing And PDF Limit Errors
Routing PDFs have two billing dimensions:
- Page count.
- Unique embedded image count.
For routing PDF scans, billing is additive. Pages cost 2 SCU each. Unique embedded images use the selected focus price:
Focused PDF SCU = pages * 2 + unique embedded images * 4
All-evidence PDF SCU = pages * 2 + unique embedded images * 12
Metadata PDF SCU = analyzed parent * 2 + distinct analyzed native PDF images * 2focus=metadata has no PDF page charge. Only native images that the metadata scan analyzed count toward its image charge. It runs synchronously in mode=secure and returns NO_DECISION; see the metadata response contract.
Tier caps are separate from SCU. A scan can be blocked before billing if the PDF exceeds the tier's page or embedded image limit.
| Condition | Typical status | Route |
|---|---|---|
| Missing payment method, quota, spending limit, or tier upgrade required | 402 | Show billing or admin action. |
| PDF exceeds allowed page count | 402 or 413 depending on entry point | Ask user to split the PDF or upgrade. |
| PDF exceeds allowed unique embedded image count | 402 or 413 depending on entry point | Ask user to reduce images, split the PDF, or upgrade. |
| Raw upload is too large | 413 | Reduce file size or route to manual review. |
Async States
| State | Meaning | Route |
|---|---|---|
pending | Deep scan is still running. | Keep pending or poll again. |
complete | Final result is ready. | Route by action. |
failed | Deep scan failed. | Manual review or safe stop. |
Retry Rules
- Honor
Retry-Afteron202polling and429capacity responses; add bounded backoff and jitter. - Retry
503only when your workflow can confirm it did not already receive a202for that scan. - Retry network errors with a unique
request_idunless your app already committed the prior request. - Do not retry
400or415until the request or file is fixed. - Do not hide
402or413. Those need product routing. - Keep webhook processing idempotent.
Ready to scan real traffic?
Book a working session on your files. Starting October 1, 2026, new commercial terms are enterprise or custom.
AI-Agent Prompt
Paste this into Cursor, Codex, Claude Code, or Windsurf.
Add Citadel error handling.
Requirements:
- Parse non-2xx responses.
- Handle 400 as a developer or request shape error.
- Handle 402 as billing, quota, or tier cap.
- Handle 409 as idempotency conflict.
- Handle 413 as file size, PDF page cap, embedded image cap, or payload complexity.
- Handle 429 with backoff.
- Treat async pending as pending, not as final.
- Treat async failed as manual review for high-risk workflows.
- Include request_id and scan_id in logs when available.
Acceptance criteria:
- Tests cover 400, 402, 409, 413, 429, network error, pending, complete, and failed.
- High-risk workflows never continue silently after a scan failure.