Errors

The error envelope every failed call returns, every code it can carry, and whether it's worth retrying.

Every failed call — through Arc0 MCP, the SDK, or REST — returns the same shape. The SDK throws this as an Arc0Error; over REST it's the JSON body.

Error envelope
{
  "error": {
    "code": "denied_by_policy",
    "message": "salesforce:destructive is denied by pol_acme_support.",
    "retryable": false
  }
}

approval_required and connection_expired add their own fields on top of this shape, shown below.

Codes

CodeHTTP statusRetryableWhat to do
denied_by_policy403NoA policy rule denies this call outright. Change the rule in Policies if it's wrong, or stop calling it.
approval_required403Yes, after it's decidedAn ask rule is holding the call. Wait for the approval.decided webhook or poll GET /v1/approvals/{id}, then retry the same call.
connection_required409NoThe user has no connection to this app. Send them an Arc0 Connect link.
connection_expired409No, until reconnectedThe refresh token is dead. The response includes a reconnect_url; send it to the user and retry once they finish.
upstream_rate_limited429YesThe app's own API rate-limited this call. Back off and retry.
upstream_invalid_request400NoThe app rejected the request shape. Fix input and try again.
upstream_timeout504YesThe app didn't respond in time. Retry with backoff.
upstream_unavailable503YesThe app's API is down. Retry with backoff.
invalid_api_key401NoThe key is wrong, revoked, or for the wrong environment. Check API keys.
insufficient_access403NoThe key's access level doesn't cover this call — for example, a Read-only key calling actions.run. Use a key with a higher level.

The upstream_* codes also carry upstream_status, the HTTP status the app itself returned, since it's often more specific than Arc0's own status.

approval_required

{
  "error": {
    "code": "approval_required",
    "message": "stripe:destructive requires approval under pol_acme_support.",
    "approval": {
      "id": "apr_9k2m",
      "requested_from": ["ops@acme.com"],
      "status": "pending",
      "expires_at": "2026-09-27T15:02:11Z"
    },
    "retryable": true
  }
}

See Approvals for how the request gets decided and what happens when you retry.

connection_expired

{
  "error": {
    "code": "connection_expired",
    "message": "The connection to salesforce needs to be reconnected.",
    "reconnect_url": "https://connect.arc0.ai/r/rc_7384l23otyjm",
    "retryable": false
  }
}

The link works once and expires in 7 days, same as the reconnect links sent from the dashboard.

What isn't an error

A call that reaches the app and gets a normal application-level failure back — an invalid Salesforce record id, for instance — isn't wrapped in this envelope. Arc0 only wraps calls it stops or holds itself; whatever the app returns on a call that actually ran is passed through as data.

On this page