atmon docs

REFERENCE/ERRORS.MD

Error codes

error_code on a tool call is a stable public contract. Codes are added, never repurposed, so a client may switch on the string and keep working across releases.

A refused call is a successful RPC. ExecuteTool answers with a ToolCall whose status and error_code say what happened; it does not raise a transport error. Read the code and act on it rather than retrying blindly.

CodeWhen it fires
rate_limitedUpstream answered HTTP 429, or the toolkit's declared rate_limit budget was empty when the call arrived. Transient: retry after the window refills. The detail ends in a retry_after_seconds value when a budget refusal or a provider Retry-After named one.
auth_expiredThe connected account is expired, refresh failed, or upstream answered 401 or 403. The end user has to authorize again.
invalid_argumentsThe tool slug is unknown, arguments_json will not unmarshal, a required path parameter is missing, or upstream answered 400 or 422. Fix the arguments; a retry of the same call fails the same way.
not_connectedThe entity holds no active connected account for the tool's toolkit. Start a connection before calling again.
deniedThe mutation gate refused a destructive tool for want of confirm: true. Tell the user what would be removed, then call again with "confirm": true inside arguments_json, beside the tool's own fields. This message declares no confirm field of its own, and one sent at the request level is ignored.
upstream_errorA transport error, a body-read error, upstream 5xx, or an unhandled status code. Transient for read tools, which execution retries.
upstream_timeoutThe external app did not answer inside the timeout, or answered HTTP 408.
internalatmon failed on its own side: a store read errored, an authorization error was unrecognized, the policy gate itself errored, or an on-prem relay answered an error code this taxonomy does not carry, which means the relay is built against another vocabulary. In the relay case the detail quotes the code the relay sent, so the row still says what happened even though the code is this platform's.
policy_deniedA policy rule refused the call, or entity visibility hides the tool from this entity. Final for the arguments as given.
velocity_exceededA policy velocity limit had no token left. Worth retrying once the window refills.
approval_pendingA policy approval gate parked the call. The response carries status TOOL_CALL_STATUS_PENDING_APPROVAL and an approval_id; retry with that id once a human approves it.
outcome_unknownAn earlier attempt claimed this call's idempotency key and never recorded an outcome, which is what a process death between the two leaves behind. The call was not repeated, because repeating it could double an effect the app already applied. Account the row as unresolved: it is never a success and never a clean failure.
resource_leasedAnother caller holds the resource this call would mutate. The detail names the holder and the expiry, so a retry is worth making once the hold ends.
connection_incompleteThe toolkit's base_url names account variables (a subdomain, an application id, a cluster address) and this account carries no value for one of them, so the connection cannot say where its calls go. The account exists and may hold a live credential, so not_connected would be wrong. Reconnect with the missing value; no retry helps.
budget_exhaustedThe project's provider quota for the toolkit is spent for the window. Distinct from rate_limited, which is a pace the provider or the client sets: this window is a budget a person wrote, so the call comes back when the window rolls or when someone raises the number. The detail names the scope whose ceiling refused it and the instant the window resets.
tool_not_in_snapshotThe project's pinned catalog snapshot does not carry this tool. Routing searches the latest catalog, so a slate can name a tool a pinned environment cannot run. Promote a snapshot that holds it rather than retrying.
provider_unavailableThe deployment holds an open incident for this tool's provider, because enough calls to one toolkit at one resolved host failed, from enough separate projects, that the provider rather than any one tenant's connection is the cause. The call was refused before it took a lease, spent a quota or resolved a credential, so its idempotency key is still free. No operator action releases it: probe traffic reopens the path as soon as the provider answers. The detail names the incident.
relay_unavailableThis tool runs on an on-prem relay, because its toolkit declares execution: relay, and the platform could not hand the call to one. No relay is connected for the selector, the relay was revoked, its work stream dropped mid-call, or the deployment connects no relay at all. The call is refused rather than queued, because a customer network this deployment cannot dial is not a wait anyone can bound. The detail names the selector and the last heartbeat, so the answer says how long the host has been gone. Nothing was sent, so the idempotency key is still free and the retry once the relay dials back in runs the call for real.

What to do with each

Every code carries its own answer, from the same source as the table above. A code that appears there and not here would be a code nobody said what to do with.

CodeRetry the same call?
rate_limitedYes, once the window refills. The retry_after_seconds value in the detail says when.
auth_expiredNo. The end user has to authorize the account again first.
invalid_argumentsNo. Fix the arguments; the same call fails the same way.
not_connectedNo. Connect an account for this entity and toolkit first.
deniedOnly with "confirm": true in the call's own arguments, after telling the user what would change.
upstream_errorYes. A read tool was already retried up to three times before this answer came back; retry a write yourself only under the same idempotency key.
upstream_timeoutYes, on the same terms as upstream_error.
internalOnce. If it repeats, the fault is on our side rather than in the call.
policy_deniedNo. A rule refused these arguments; different arguments may pass.
velocity_exceededYes, once the limit's window refills.
approval_pendingYes, carrying the approval_id, once a human approves it.
outcome_unknownNot blindly. The effect may already have landed, so read the app's own state before deciding whether to call again.
resource_leasedYes, once the hold the detail names expires.
connection_incompleteNo. Reconnect the account with the missing value first.
budget_exhaustedNo, until the window the detail names rolls or somebody raises the ceiling.
tool_not_in_snapshotNo. Promote a catalog snapshot that carries the tool.
provider_unavailableYes, and nothing has to be done first: probe traffic reopens the path as soon as the provider answers.
relay_unavailableYes, once the relay dials back in. Nothing was sent, so the call runs for real.

Statuses

ToolCall.status is coarser than the code and is what a UI shows:

StatusMeaning
TOOL_CALL_STATUS_SUCCEEDEDThe external app answered and the result is shaped to the output schema.
TOOL_CALL_STATUS_FAILEDThe call ran and failed. error_code says how.
TOOL_CALL_STATUS_DENIEDRefused before any credential was resolved: the mutation gate or a policy rule.
TOOL_CALL_STATUS_PENDING_APPROVALParked on a human. approval_id is what releases it.
TOOL_CALL_STATUS_RUNNINGIn flight.

Every one of these is recorded. A denial and a park leave a receipt exactly like a success, which is what makes the record of what happened complete rather than best-effort.

The limits behind four of these codes, and what to do about each, are in Limits. What an assistant should say when it meets one is in The four verbs.