API Error Codes
AxonFlow does not have one universal error enum shared perfectly across every handler. Different subsystems use slightly different response helpers. What is consistent enough for engineers to build against is:
- standard HTTP status codes
- common JSON error envelopes on most
api/v1handlers - recurring error codes such as
VALIDATION_ERROR,NOT_FOUND,INTERNAL_ERROR,UNAUTHORIZED,CONFLICT,TIER_RESTRICTEDand, in v11,LEGACY_POLICY_WRITE_FROZEN
That means the safest production integration strategy is:
- branch on HTTP status first
- inspect
error.codewhen present - log the full body for operator debugging
Common HTTP Statuses
| Status Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request successful |
| 201 | Created | Resource created |
| 204 | No Content | Delete or success without a body |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing auth or missing tenant scope on some handlers |
| 403 | Forbidden | Tier restriction, proxy restriction, or governance denial |
| 404 | Not Found | Endpoint or resource not found |
| 409 | Conflict | Duplicate resource or incompatible state; in v11 also a write to the legacy policy families (LEGACY_POLICY_WRITE_FROZEN) |
| 415 | Unsupported Media Type | check-input called with a content_type that has no registered redaction detector (fail-closed governance refusal) |
| 500 | Internal Server Error | Unexpected server error |
| 503 | Service Unavailable | A dependency is unavailable; in v11 also a request whose caller identity could not be established (subject_unverifiable) |
Common Error Envelope
Many orchestrator handlers return:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed"
}
}
Some older handlers instead return plain string bodies such as:
Connector not found
or:
{
"error": "Invalid request body"
}
So production clients should not assume every route returns the exact same envelope.
Frequently Seen Error Codes
| Error code | Where you will see it |
|---|---|
VALIDATION_ERROR | Policy APIs, provider APIs, and other typed control-plane handlers |
INVALID_JSON | Template and policy handlers when the request body is malformed |
INVALID_REQUEST | Media governance and some gateway or legacy handlers |
UNAUTHORIZED | Missing tenant scope or auth on structured handlers |
TENANT_REQUIRED | Tenant-scoped handlers called without X-Tenant-ID. Added in platform v7.2.0. |
NOT_FOUND | Missing providers, policies, templates, executions, or steps |
CONFLICT | Duplicate provider names or resource conflicts |
LEGACY_POLICY_WRITE_FROZEN | v11: any write to the legacy system-policy, tenant-policy or /api/v1/policies families, and applying a policy template. Returns 409; the message names /api/v1/typed-policies. See Legacy policy write refused. |
IDEMPOTENCY_KEY_MISMATCH | WCP /gate or /complete supplied an idempotency_key that does not match the key recorded on the step's first gate call. Returns 409. See Retry Semantics & Idempotency. |
LICENSE_ERROR | License-gated provider creation failures |
TIER_RESTRICTED | Paid-tier-only config such as media governance writes |
FEATURE_REQUIRES_EVALUATION_LICENSE | Evaluation-tier-or-higher feature attempted on a Community license. Examples: evidence export, policy simulation, retry-aware policy conditions (step.* fields on WCP policies). Returns 403. Upgrade path: getaxonflow.com/evaluation-license. |
INTERNAL_ERROR | Backend or storage failures |
Practical Guidance
- Treat
400and401as client-fixable issues. - Treat
403as either a governance restriction, a tier restriction, or a deployment routing issue. - Treat
404as a missing resource or a wrong endpoint. - Treat
500as retriable operational failure after logging enough context.
For example:
POST /api/v1/tenant-policiesreturns409 LEGACY_POLICY_WRITE_FROZENin v11POST /api/v1/llm-providerscan returnCONFLICTorLICENSE_ERRORPOST /api/v1/workflows/{id}/steps/{step_id}/gateand/completecan return409 IDEMPOTENCY_KEY_MISMATCHwhen the suppliedidempotency_keydoes not match the key recorded on the step's first gate callPUT /api/v1/media-governance/configcan returnTIER_RESTRICTED- replay routes can return
NOT_FOUNDfor missing executions or steps
Missing Tenant Scope (401)
Platform v7.2.0 adds a dedicated TENANT_REQUIRED response for tenant-scoped handlers that previously fell back to an empty-string tenant when neither X-Tenant-ID nor a session-stored tenant was set. The fallback silently scoped SQL to WHERE tenant_id = '', so legitimate calls without the header returned zero rows while sharing a global empty-tenant quota bucket with every other unauthenticated caller.
{
"error": {
"code": "TENANT_REQUIRED",
"message": "Missing tenant context: caller must provide X-Tenant-ID header"
}
}
Handlers that now fail closed with the structured 401 TENANT_REQUIRED response above:
POST /api/v1/evidence/exportGET /api/v1/evidence/summaryPOST /api/v1/policies/simulatePOST /api/v1/policies/impact-reportPOST /api/v1/plans/estimateGET /api/v1/plans/{id}/cost
A few other tenant-scoped handlers also require X-Tenant-ID and reject the call with 401 when it is missing, but return a plain-text error message rather than the structured TENANT_REQUIRED envelope:
GET /api/v1/decisions/{id}/explain—401with body{"error": "X-Tenant-ID header is required"}POST /api/v1/audit/search—401with body{"error": "tenant scoping required"}GET /api/v1/audit/tenant/{tenant_id}—401with body{"error": "X-Tenant-ID header is required"}plus a403 tenant scope mismatchwhen the URL tenant differs from the header
In practice the agent proxy injects X-Tenant-ID from the authenticated Basic-auth client, so well-formed SDK and Portal traffic never sees either error shape. They surface most often when a caller hits the orchestrator directly without the proxy-auth layer.
Legacy policy write refused (409)
In v11 the legacy system-policy and tenant-policy rows are read-only. A write to either family, through the Agent, the Orchestrator or the Customer Portal, and an apply of a policy template, answers:
{
"error": {
"code": "LEGACY_POLICY_WRITE_FROZEN",
"message": "The legacy policy tables are read-only in v11: migrations/core/172 revoked write access from the application role, and this endpoint writes them. Author policies through the typed authoring route at /api/v1/typed-policies instead. Reads on this endpoint are unaffected."
}
}
Do not retry it: no licence, edition or upgrade changes the answer. Author the policy through typed policy authoring instead.
Identity refusals (401, 403, 503)
In v11 every governed request's caller is admitted through the identity plane after it authenticates:
- A bad credential is refused
401before any policy runs, and so is a user token whose signature fails on a deployment that verifies user identity. - A user token the identity plane does not admit is refused with the identity reason: a
denyverdict onPOST /api/v1/decide,403on Gateway Mode's pre-check and on Proxy Mode'sPOST /api/request, and a withheld403on the MCP response pass. The reasons a caller will meet includeUNKNOWN_REALM(the token's issuer is declared by no trust realm, or it has no issuer),ORG_BINDING_MISMATCH,SUBJECT_MISSING,CREDENTIAL_EXPIRED,CREDENTIAL_NOT_YET_VALIDandCREDENTIAL_REVOKED. - When admission cannot reach an answer, for example because a revocation source is unavailable, the request fails closed with
503andsubject_unverifiable.
A request with no user token is decided for its client credential and is not refused for the missing token. On Community and Community SaaS, which verify no per-user identity, a user token is ignored and every request is decided for its client credential, so none of these identity refusals applies there. Identity Compatibility Mode lists what changed in v11.
Policy Denial (403)
Returned when a governance policy blocks the request. The body differs by plane, and the safe integration assumption is that policy denials are not normalized to one shared POLICY_DENIED envelope everywhere. On POST /api/v1/decide a refusal is a 200 response whose verdict is deny and whose reasons carry the engine's reason, for example explicit_constraint (a policy whose action is block matched) or unsupported_obligation (a mandatory safeguard the calling enforcement point cannot carry out).
Rate Limiting (429)
Returned when the client exceeds configured rate limits:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"details": {
"limit": 100,
"remaining": 0,
"reset_at": "2026-03-29T12:00:00Z"
}
}
}
MCP Policy Block (403)
Returned by check-input or check-output when MCP policy evaluation blocks:
{
"allowed": false,
"block_reason": "Blocked by policy corpus:static_policies:sql__injection__union",
"policies_evaluated": 3
}
Note: MCP check endpoints return a flat response (not the nested error envelope).
Unsupported Content Type (415)
POST /api/v1/mcp/check-input rejects a request whose content_type names no registered redaction detector, so a caller cannot forward content the engine cannot govern:
{
"success": false,
"error": "no redaction detector registered for content_type: image/png",
"blocked": false
}
An empty or omitted content_type defaults to the built-in text detector, so existing callers never see this status.
SDK Error Handling
Python
from axonflow.exceptions import (
AxonFlowError,
PolicyViolationError,
RateLimitError,
AuthenticationError,
)
try:
response = client.proxy_llm_call(...)
except PolicyViolationError as e:
print(f"Blocked by policy: {e.block_reason}")
except RateLimitError as e:
print(f"Rate limited, retry after: {e.reset_at}")
except AuthenticationError:
print("Invalid credentials")
except AxonFlowError as e:
print(f"API error {e.status_code}: {e.message}")
TypeScript
import { AxonFlowError } from '@axonflow/sdk';
try {
const response = await axonflow.proxyLLMCall({...});
} catch (error) {
if (error instanceof AxonFlowError) {
if (error.statusCode === 403) {
console.error('Policy blocked:', error.message);
} else if (error.statusCode === 429) {
console.error('Rate limited');
}
}
}
Go
response, err := client.ProxyLLMCall("user-123", "query", "chat", nil)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if response.Blocked {
fmt.Printf("Blocked by policy: %s\n", response.BlockReason)
}
Related Docs
Operational Readiness Checklist
Before relying on this page in a production rollout, pair it with the core operations docs:
- Deployment Mode Matrix for self-hosted, Evaluation, Enterprise, SaaS, and In-VPC fit
- Failure Modes And Recovery for degraded-provider, connector, approval, and runtime behavior
- Capacity Planning for sizing and growth signals
- Community vs Evaluation vs Enterprise for limits, support surfaces, and upgrade triggers
