Every ARCP error maps to an ErrorCode constant (RFC §18.2). Catch
ARCPException subclasses to react to them:
try {
client.open()
} catch (e: ARCPException.Unauthenticated) {
println("Auth failed: ${e.message}")
} catch (e: ARCPException.BudgetExhausted) {
println("Budget exhausted (${e.currency}) on job ${e.jobId}")
}| Wire code | Retryable? | Exception class | Common cause |
|---|---|---|---|
OK |
— | — | Success (non-error) |
CANCELLED |
No | ARCPException.Cancelled |
Client cancelled the operation |
UNKNOWN |
No | ARCPException.Unknown |
Unexpected server-side error |
INVALID_ARGUMENT |
No | ARCPException.InvalidArgument |
Malformed request field |
DEADLINE_EXCEEDED |
Yes | ARCPException.DeadlineExceeded |
Operation timed out |
NOT_FOUND |
No | ARCPException.NotFound |
Resource or agent does not exist |
ALREADY_EXISTS |
No | ARCPException.AlreadyExists |
Duplicate message_id in event log |
PERMISSION_DENIED |
No | ARCPException.PermissionDenied |
Missing lease or permission |
RESOURCE_EXHAUSTED |
Yes | ARCPException.ResourceExhausted |
Rate-limit hit (wire alias: RATE_LIMITED) |
FAILED_PRECONDITION |
No | ARCPException.FailedPrecondition |
Operation not valid in current state |
ABORTED |
Yes | ARCPException.Aborted |
Concurrency conflict; retry |
OUT_OF_RANGE |
No | ARCPException.OutOfRange |
Value exceeds valid bounds |
UNIMPLEMENTED |
No | ARCPException.Unimplemented |
Feature not yet implemented |
INTERNAL |
Yes | ARCPException.Internal |
Runtime bug; report it |
UNAVAILABLE |
Yes | ARCPException.Unavailable |
Runtime temporarily unreachable |
DATA_LOSS |
No | ARCPException.DataLoss |
Message missing from event log |
UNAUTHENTICATED |
No | ARCPException.Unauthenticated |
Bad or missing bearer/JWT token |
HEARTBEAT_LOST |
Yes | ARCPException.HeartbeatLost |
Job missed consecutive heartbeat deadlines |
LEASE_EXPIRED |
No | ARCPException.LeaseExpired |
Lease TTL passed before use |
LEASE_REVOKED |
No | ARCPException.LeaseRevoked |
Runtime revoked the lease |
LEASE_SUBSET_VIOLATION |
No | ARCPException.LeaseSubsetViolation |
Requested capability exceeds granted subset |
BUDGET_EXHAUSTED |
No | ARCPException.BudgetExhausted |
Cost budget ceiling reached |
AGENT_VERSION_NOT_AVAILABLE |
No | ARCPException.AgentVersionNotAvailable |
No matching agent@version registered |
BACKPRESSURE_OVERFLOW |
Yes | ARCPException.BackpressureOverflow |
Subscription channel full |
The bearer token was rejected. Check:
- Token is not empty or whitespace.
- Server's
StaticBearerAuthmap includes the exact token. - For JWT: the
subclaim is non-blank,audmatches the configured audience, and the token has not expired.
The client requested agent@version that is not registered:
// Runtime must register the agent + version before accepting connections
val registry = AgentRegistry()
registry.register("summarise", "1.0.0", default = true)
val runtime = ARCPRuntime(
supportedCapabilities = Capabilities(),
agentRegistry = registry,
)The job's provisioned credential contained a cost.budget cap that was
reached. The exception carries the currency and jobId:
} catch (e: ARCPException.BudgetExhausted) {
logger.warn { "Job ${e.jobId} exceeded ${e.currency} budget" }
}The runtime stopped receiving heartbeat acknowledgements from a job. The
missedDeadlines property shows how many consecutive beats were missed.
If the job is an external subprocess, check that it is calling
JobHeartbeat on schedule.
EventLog.replay() could not find afterMessageId in the log for the given
session. This usually means the log was deleted or the wrong session ID was
used. Verify the SQLite file path and the session_id value.
The Gradle wrapper cannot find JDK 21. Fix:
export JAVA_HOME=/opt/homebrew/opt/openjdk@21
export PATH="$JAVA_HOME/bin:$PATH"
./gradlew buildRun detekt locally to see the specific rule:
./gradlew :lib:detektCommon fixes:
- FunctionTooLong — extract helpers; target ≤15 lines, hard cap 30.
- ComplexMethod — extract branches into named functions or
whentables. - MagicNumber — move numeric literals to
const val. - ForbiddenVoid — use
Unitinstead ofvoid-style expressions.