Skip to content

Errors and reason codes ​

Every API error is an RFC 9457 problem document with content type application/problem+json. The type URL points at the matching section of this page, so https://docs.sectigoaxiom.com/reference/errors#idempotency_conflict lands on idempotency_conflict.

json
{
  "type": "https://docs.sectigoaxiom.com/reference/errors#idempotency_conflict",
  "title": "idempotency conflict",
  "status": 409,
  "code": "idempotency_conflict",
  "detail": "operation key reused with different material parameters; nothing was changed",
  "reason_code": "material_change",
  "changed_fields": ["params.amount"],
  "action_id": "act_01kd0m7s2v8x9yz3abcdefghjk"
}
FieldPresent when
codeAlways. A stable error code; branch on this, not on title or detail.
reason_codeThe error is a policy or lifecycle decision (for example tenant_mismatch, wrong_resume_actor).
changed_fieldsidempotency_conflict on submit, or conflict on resume: the material fields that differ.
action_idThe error refers to an existing action.
issuesvalidation_failed: { field, message } per failing field (dotted paths, e.g. params.amount).
retry_after_secondsA retry can succeed later. Also sent as the Retry-After header.

Two other error shapes exist:

  • The OAuth endpoints (/oauth/token, /oauth/register, /oauth/authorize) answer with RFC 6749 §5.2 bodies: { "error": "invalid_client", "error_description": "…" }. Codes used: invalid_client (401), invalid_dpop_proof (401), invalid_grant, invalid_request and unsupported_grant_type (400). The SDK maps both shapes onto one AxiomProblem with a code.
  • MCP endpoints answer with JSON-RPC errors; a domain error inside a tool becomes a tool result with isError: true and the problem document as structuredContent. See MCP.

A caller that cannot see a resource always gets 404 not_found, never 403, so the response does not reveal whether the resource exists.

Error codes ​

Defined in packages/core/src/errors.ts (ERROR_CODES).

unauthenticated ​

401. No credential was presented where one is required (agent endpoints, console endpoints, the Gateway proxy). The proxy and MCP endpoints add a WWW-Authenticate header: DPoP algs="ES256 EdDSA" on the Gateway proxy, and Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp" on the Axiom MCP server so MCP clients can discover the authorization server.

What to do: authenticate. Agents get a DPoP-bound token (Authentication); people sign in to the console or use the OAuth authorization-code flow.

invalid_token ​

401. The access token is unknown, expired (agent tokens last 5 minutes), revoked, presented with the wrong scheme (Bearer for a DPoP-bound token), issued for a different MCP resource, or its credential has been revoked or has expired (reason_code: credential_revoked). Also returned for an invalid or revoked project API key.

What to do: get a new token and retry once. The SDK does this automatically. If the new token is rejected too, the credential itself is no longer valid: see Lost or compromised keys.

invalid_dpop_proof ​

401. The DPoP header is missing, malformed, signed by a key other than the one the token is bound to, has the wrong htm or htu, an iat more than 120 seconds from server time, a wrong ath, or a jti that was already used (replay). On /v1/authorize it also means the proof was not made for the service's upstream origin.

What to do: fix the proof; do not retry in a loop. Check the clock, that htu is the exact URL without query string, that ath is the base64url SHA-256 of the token, and that every request uses a fresh jti.

forbidden ​

403. The caller is authenticated but the request is refused. Usually carries a reason_code: tenant_mismatch (the request names another tenant), wrong_resume_actor, agent_suspended, issuer_not_trusted, separation_of_duties, approver_ineligible, delegation reasons, or a Gateway proxy refusal (operation_not_permitted for an unmapped route, or a denied action). Also used when a token lacks a required scope (actions:submit) or a project API key lacks provider:authorize.

What to do: read reason_code, report it and stop. A different request, credential or grant is needed; retrying the same call will not succeed.

not_found ​

404. The resource does not exist, or the caller may not see it. Agents get 404 for other agents' actions; people get 404 for projects, tenants and approvals outside their roles. Also returned when an operation is not protected on the named service.

What to do: treat the resource as absent. Never guess IDs.

conflict ​

409. The request conflicts with the record. On resume: the parameters, resource or operation key differ from the approved request (reason_code: material_change, changed_fields). On the approval page: the request changed after the approver viewed it. Control endpoints use it for, for example, a domain verified by another organisation.

What to do: do not "fix" the parameters to match. A changed request needs a new submit with a new operation key and a fresh approval.

idempotency_conflict ​

409. The operation key is already used for a different request in this tenant and service: either a material field differs (reason_code: material_change, with changed_fields and the existing action_id) or another agent used the key. The existing action is unchanged.

What to do: the original action stands. Report changed_fields. If the change is intended, it is a new intent: choose a new key. See Idempotency and conflicts.

invalid_state ​

409. The action is in the wrong state for the call: resuming before approval (with retry_after_seconds and the current reason_code), resuming a closed action, cancelling after dispatch, or reporting an outcome for an action that is not claimed. Two execution-ownership rules also answer invalid_state, on every Axiom path (REST resume, the HTTP and MCP proxies and the Axiom MCP server):

  • the service runs in embedded-SDK mode, so only the provider may claim: repeat the original request to the provider with the same operation key;
  • after a cutover, a customer-hosted gateway holds the project's executor flag: resume through that gateway.

The Gateway proxy also uses it for an expired or cancelled action.

What to do: read status and follow the state table in Action state machine.

precondition_failed ​

412. Reserved for conditional requests. No endpoint in the current release returns it.

invalid_request ​

400. The request is malformed: the body is not JSON, a gateway request body exceeds 1 MiB, a required Idempotency-Key is missing on a proxied write, a challenge is expired or already used, a redirect URI is not allowed, or a connector does not support the requested operation.

What to do: fix the request.

validation_failed ​

422. The body failed schema validation, or material fields failed the operation's field specification (wrong type, missing required field, undeclared field, decimal not a string, currency not ISO 4217, wrong resource type). issues lists each field.

What to do: fix the fields in issues. Decimals are strings ("25000.00"), currencies are three upper-case letters.

rate_limited ​

429. Too many attempts: sign-in (magic links, passkeys) or the free public services (for example 30 identity checks per minute per client address). Carries retry_after_seconds and Retry-After.

What to do: wait for retry_after_seconds.

quota_exceeded ​

402. The project's plan limit is reached: new actions in the month, or another protected service. Existing actions, approvals, status, resume and receipts keep working. Paid plans with a spend limit above zero can exceed included actions. See Plans and limits.

What to do: report to the user; ask the project owner to raise the plan or spend limit.

upstream_error ​

502. A system Axiom depends on failed: for example the enterprise identity provider rejected an SSO code.

What to do: retry later; if it persists, check the upstream system.

upstream_timeout ​

504. The Gateway proxy dispatched the request but the upstream did not answer (transport error or timeout after 20 seconds). The action is now outcome_unknown (reason_code: target_timeout) and reconciliation by operation key is queued. The response carries Axiom-Action-Id.

What to do: never resend. Track the action ID until reconciliation restores a final state. See Execution and delivery.

internal ​

500. An unexpected server error. The x-request-id response header identifies the request.

What to do: retry a read. For a submit, retry with the same operation key, which is always safe. For a resume, read status before doing anything else.

Reason codes ​

Reason codes (REASON_CODES in packages/core/src/states.ts) explain a decision. They appear as reason_code on every action status and on problem documents that are decisions.

Allow ​

CodeMeaningWhat to do
allowed_by_grantThe grant covers the request and no policy layer requires approval or denies it.Nothing: within a standing grant the action is dispatched at once (unless execute: false).

Approval and progress ​

CodeMeaningWhat to do
approval_requiredA rule requires approval and does not name a more specific reason.Persist the action ID and wait.
finance_approval_requiredA rule requiring finance approval matched (for example a refund above the threshold).Persist, tell the user who can review it, wait.
second_approver_requiredOne approval was recorded but the rule requires more distinct approvers.Keep waiting; a different person must approve.
awaiting_deliveryReserved. Delivery progress is reported as progress: delivery_pending.Wait.
awaiting_decisionReserved. Reported as progress: waiting_on_person.Wait.
decision_readback_unavailableReserved. Reported as progress: decision_readback_unavailable when the connector cannot be read.Wait; an admin should check the connector.
awaiting_agent_resumeEvery required approval is recorded; the action is approved and waits for the original agent.Resume as the original agent before resume_deadline.

Deny and close ​

CodeMeaningWhat to do
no_grantThe agent holds no grant on this service.Stop. A tenant admin must grant access.
grant_expiredThe grant expired (at submit, or found expired when the agent resumes).Stop; submit a new request once authority exists.
grant_revokedThe grant, a parent grant in its delegation chain, or a suspended grant no longer authorises.Stop.
grant_scope_mismatchThe grant is not yet valid, belongs to another agent, or a child grant is bound to another root action.Stop.
tenant_mismatchThe request names a tenant other than the authenticated agent's, or the grant is for another tenant.Stop; tenant always comes from the credential.
audience_mismatchThe grant is for a different service.Stop.
resource_not_permittedThe resource is outside the grant's resource patterns.Stop.
operation_not_permittedThe grant does not include the operation, or (Gateway proxy) the route or tool is not mapped.Stop.
agent_suspendedThe agent is suspended (or not active).Stop; see Disabled agents.
agent_retiredThe agent is retired.Stop; a retired agent cannot return.
credential_revokedThe credential used for the action or token was revoked or expired.Re-authenticate with an active key; an action created with a revoked credential is revoked at resume.
issuer_not_trustedA workload identity's issuer is not in the tenant's trust profile.Ask the tenant admin to add it.
ceiling_exceededA hard ceiling rule matched; no approval or override can lift it.Stop.
policy_denyA deny rule matched and names no more specific reason.Stop.
emergency_denyAn incident published an emergency deny covering the agent, credential, grant, tenant, service or project.Stop; see Safe release after containment.
delegation_not_permittedThe parent grant is not delegable, or the child is the same agent.Stop.
delegation_expands_scopeThe child grant would outlive the parent or widen operations or resources.Request a narrower child grant.
delegation_depth_exceededThe parent is already a delegated grant; one hop is supported.Stop.
wrong_resume_actorA different agent tried to resume. Only the original agent can.Stop.
material_changeThe request differs from the approved or recorded one.Submit a new request with a new key.
approval_deniedAn approver denied the request.Report and stop.
approval_expiredNo verified decision arrived before decision_deadline, or the request validity ended.Report and stop; a new request is needed.
approver_ineligibleAt resume, a recorded approver no longer holds the required role; or the caller cannot decide this approval.Stop; a fresh approval is needed.
separation_of_dutiesThe approver owns or sponsors the agent, or authored the policy that required approval.A different person must approve.
policy_stalePolicy changed after approval and now requires an approval that was not satisfied.Submit a new request for fresh approval.
status_staleReserved for freshness-bounded decisions at customer-hosted gateways.Stop and retry once the gateway is fresh.
quota_exceededReserved for quota decisions; quota errors are returned as the quota_exceeded error code.See the error code.
invalid_requestReserved; used as the reason on a proxied write without Idempotency-Key.Fix the request.

Execution ​

CodeMeaningWhat to do
dispatchedExecution was claimed (executing) and sent to the target.Track by status; never resend.
target_confirmedThe action succeeded and the provider's signed outcome attestation confirms it.Read the outcome and receipt.
target_failedThe action failed and the provider's signed attestation says the business write did not commit.Read the outcome.
gateway_observed_successThe action succeeded on an unsigned 2xx: the gateway observed the response; the destination did not confirm it, and the receipt's outcome check is missing.Read the outcome; register outcome keys on the service for destination confirmation.
gateway_observed_failureThe action failed on an unsigned 4xx (other than 408 and 429): the gateway observed a rejection that the destination did not attest.Read the outcome; a new attempt needs a new operation key.
target_timeoutThe target did not answer, answered 5xx, 408 or 429, or returned an invalid attestation: outcome_unknown.Track; reconciliation by operation key is running.
resume_expiredThe action was approved but the original agent did not resume before resume_deadline (or the request validity ended).Stop; a new request needs fresh approval.
reconciledReconciliation restored a final state from the provider's signed record.Read the outcome and receipt.
cancelled_by_requesterThe original agent cancelled before dispatch.Nothing further happens.

The fine-grained progress field on a status adds: delivery_pending (the approval request is being delivered), waiting_on_person, decision_readback_unavailable (the connector could not be read; it is retried every 60 seconds), awaiting_agent_resume and reconciling.

Developer preview. Contract version 2026-11-14.