Skip to main content

Exceptions

All exceptions live in arclasp.exceptions. The base class is ArclaspPolicyError; catching it catches every policy-related exception the SDK raises. A few exceptions extend Exception directly when they’re not policy decisions (timeouts, backend connectivity, auto-pause).
Catch ArclaspPolicyError when you want to handle every policy-driven denial uniformly:
For operational issues (backend down, chain timed out, runaway behavior auto-paused), catch the relevant Exception subclass instead.

ActionDeniedError

Raised when a policy denies an action, when a human reviewer denies an approval, or when the backend marks a pending approval as timed out. Backend unavailability is a separate case — see BackendUnavailableError below.

Attributes

Distinguishing denial reasons

ActionDeniedError doesn’t have a single reason enum. To tell what kind of denial occurred, check decision_source and condition together:

Example

Don’t suppress and continue

Suppressing ActionDeniedError and retrying the same action accomplishes nothing — the policy will deny it again. If you catch it, either:
  1. Change the situation (lower amount, different recipient, different agent) and retry
  2. Escalate the work to a human
  3. Log the denial and continue with the next part of your workflow
The message, remediation, and docs_url fields are built to be shown to your application’s user — they tell a human reading logs what to do.

PolicyViolationError

Raised when a policy condition is violated outside the normal action-decision path — for example, when chain-level cumulative limits are checked at chain start or during background reconciliation rather than at an individual action evaluation. Inherits from ArclaspPolicyError. In practice, most application code can catch ArclaspPolicyError to handle this and ActionDeniedError uniformly.

ArclaspKillSwitchError

Raised when the organization’s kill switch is active and an action is attempted.

When raised

Whenever the kill switch is on for your org and record_agent_action is called. This is distinct from a regular ActionDeniedError so applications can show a different message (maintenance mode vs. policy violation).

Attributes

Example

See Kill switch for activation and resumption.

BackendUnavailableError

Raised when the SDK can’t reach the backend after retries (max_retries, backend_timeout_seconds) or the backend keeps returning 5xx/429. Inherits from Exception, not ArclaspPolicyError — it’s an operational issue, not a policy decision. This is always raised on backend unavailability, regardless of fail_mode / fail_modes. fail_mode="allow" is accepted for call-signature compatibility but is deprecated and has no effect — it emits a DeprecationWarning and the SDK still fails closed. There is no local buffering or “proceed and sync later” behavior in governed execution; the action simply did not run.

Example


ChainTimeoutError

Raised when a chain exceeds its configured runtime without completing. Inherits from Exception. Long-running batch workflows should configure a longer timeout via SDK config. The exception is meant to catch chains that have hung — not chains that are doing legitimate long work.

ChainAutoPausedError

Raised when a chain hits one of the auto-pause limits — event count, chain duration, or token budget. Inherits from Exception. See Policies for the default auto-pause thresholds.

Example


Transport and setup errors

Not every failure is a policy decision. Two plain-Exception cases come from lower in the stack:
  • RuntimeError — raised by the SDK itself for setup mistakes: calling any Chain method before arclasp.init(), or before entering the chain as a context manager ("Chain has not been started. Use it as a context manager."), or using synchronous with Chain(...) from inside a running async event loop.
  • httpx.HTTPStatusError — raised directly (not wrapped) on 4xx responses, since those are deterministic client errors that retrying won’t fix. The two you’ll see most: 401 (missing or invalid API key) and 403 (the key’s role doesn’t have permission for that operation).

Troubleshooting

Hosted example accidentally hitting localhost. backend_url defaults to http://localhost:8000. If you copied an example without adding backend_url="https://api.proofrail.dev", every call will fail to connect — you’ll see a connection error surface as BackendUnavailableError after retries are exhausted, not a clean “wrong URL” message. Check your arclasp.init() call first. 401 — missing or invalid API key. httpx.HTTPStatusError with status_code=401. Confirm ARCLASP_API_KEY is set and that the key hasn’t been revoked from the dashboard. 403 — role/permission problem. httpx.HTTPStatusError with status_code=403. The authenticated key or user doesn’t have the role required for that endpoint (e.g., approving requires the approver role). Approval still pending / terminal waiting. This is expected while record_agent_action() blocks on a require_approval decision — it polls every 5 seconds. Open the dashboard’s /dashboard/approvals and resolve the pending item; the terminal resumes on the next poll after you do. Approval denied. record_agent_action() raises ActionDeniedError with decision_source="human_approval" and the reviewer’s notes in condition. Approval timed out. Either ActionDeniedError (the backend marked the approval as timed out) or ChainTimeoutError (the SDK’s own local polling window, default_approval_timeout_hours, expired first). See Distinguishing denial reasons above.

Handling patterns

Catch all policy denials, distinguish by source

Distinguish human denial from timeout

Log denials for analytics

Tracking which policies deny most often is a strong signal for what to revisit — either the policy is wrong, the workflow is wrong, or the thresholds need adjusting.

Don’t catch what you can’t fix

ArclaspKillSwitchError and ChainAutoPausedError shouldn’t be caught and worked around in normal application code:
  • ArclaspKillSwitchError is an admin saying “stop everything”. Honor it; don’t bypass.
  • ChainAutoPausedError means runaway behavior was detected. Stop and investigate; don’t retry blindly.
ActionDeniedError is the exception you’d routinely catch in application logic. BackendUnavailableError is the one to catch when you need explicit retry or queue handling.

Where to go next

SDK API

Type signatures for everything else.

Configuration

The init() options that drive these errors.

Human approval

Where approval-related denials come from.

Kill switch

Activating, resuming, and handling in code.