Skip to content

08. Error Codes

Source of truth: packages/shared/src/errors.ts (ErrorCodes). Codes in §3.6 are reserved (documented ahead of emission); everything else is live.

1. Goal

Provide one stable error vocabulary across:

  • Renderer UI
  • Electron IPC
  • Rust host RPC
  • Node pi sidecar bridge

2. Error object

ts
type AppError = {
  code: string            // stable machine code, e.g. TOOL_DENIED
  message: string         // English UI/default message
  details?: unknown
  retriable?: boolean
  source?: "renderer" | "electron" | "host" | "agent" | "plugin"
  causeCode?: string      // nested/transport code if mapped
  traceId?: string
}

Rules:

  1. code is immutable once published
  2. message is English source text (i18n key may map separately)
  3. UI should prefer i18n key derived from code when available

3. Code registry

3.1 App / protocol

coderetriablemeaning
PROTOCOL_MISMATCHnohandshake/protocol version mismatch
HOST_UNAVAILABLEyesRust host not running/reachable
HOST_OVERLOADEDyesbounded host RPC/tool capacity is full; retry after backpressure
AGENT_UNAVAILABLEyespi sidecar not running/reachable
APP_DEGRADEDyesapp running with limited capabilities
INTERNALmaybeunexpected internal failure
INVALID_ARGUMENTnorequest schema/args invalid, including a native-tool path of the wrong file/directory kind
UNAUTHORIZEDnocapability/auth boundary rejected call
NOT_FOUNDnoentity not found
CONFLICTmaybestate conflict / busy resource
TIMEOUTyesgeneric timeout

HOST_UNAVAILABLE is reserved for a missing or broken host process/transport, not ordinary admission pressure. RPC capacity returns HOST_OVERLOADED, and an admitted shell that cannot start because the OS is temporarily out of process resources returns PROCESS_RESOURCE_EXHAUSTED. Host-core's control stdio is isolated from Tokio's dynamic blocking pool so the latter condition does not turn temporary thread pressure into a host process exit.

3.2 Agent / session

coderetriablemeaning
AGENT_BUSYnosession already has active turn
AGENT_NOT_FOUNDnosession missing
TURN_NOT_FOUNDnoturn id invalid
TURN_ABORTEDnoturn aborted by user/system
MODEL_NOT_CONFIGUREDnono usable model selected, or provider rejects the selected model as unknown
PROVIDER_ERRORyesupstream provider failure
PROVIDER_UNAUTHORIZEDnobad/missing provider credentials
PROVIDER_RATE_LIMITEDyesprovider rate limited
CONTEXT_TOO_LARGEnoprompt/context still exceeds the safe model budget after recovery, the second provider overflow occurred, or automatic recovery is disabled
CONTEXT_COMPACTION_FAILEDnoautomatic retained-tail recovery could not prepare, persist, or fit a checkpoint, or manual checkpoint summary generation / durable append failed; the guarded next provider request does not start
STREAM_FAILEDyesprovider stream was terminated, closed prematurely, or otherwise ended before a complete response; one same-turn retry may precede the terminal event
EMPTY_MODEL_RESPONSEyesthe model ended its turn with no tool call and no visible text twice: once as streamed, once after the automatic re-run (spec 02-agent-runtime §5e)

3.3 Workspace / tools / permissions

coderetriablemeaning
WORKSPACE_REQUIREDnono workspace bound
PATH_OUTSIDE_WORKSPACEnopath escapes sandbox before an explicit outside-path permission decision, or a non-permissioned compatibility call reaches the resolver
TOOL_NOT_FOUNDnounknown tool
TOOL_DENIEDnopermission denied / mode forbidden
TOOL_TIMEOUTyestool execution timeout
TOOL_FAILEDmaybetool executed but failed
PROCESS_RESOURCE_EXHAUSTEDyesshell process could not start because the OS temporarily exhausted process resources
SHELL_NOT_FOUNDnono effective platform shell is available after catalog fallback; message carries guidance
COMMAND_SHELL_CHANGEDnopinned shell ID or dialect changed before execution
COMMAND_SHELL_INVALIDnosettings supplied an unknown, unavailable, or wrong-platform shell ID
PERMISSION_TIMEOUTnopermission prompt timed out (mapped to deny)
PERMISSION_REQUIREDnowaiting for user decision
WRITE_DISABLED_IN_PLANnocontract-mode hard-deny for Write
EDIT_DISABLED_IN_PLANnocontract-mode hard-deny for Edit
PLUGIN_DISABLED_IN_PLANnocontract-mode hard-deny for every plugin tool
TOOL_DISABLED_IN_PLANnocontract-mode hard-deny for an unknown/unlisted tool
PLAN_NOT_ACTIVEnoa submit tool ran while no contract was being negotiated
PLAN_KIND_MISMATCHnoSubmitPlan in Goal mode, or SubmitGoal in Plan mode
PLAN_APPROVAL_REQUIREDnoSubmitPlan/SubmitGoal is waiting for a separate approval
PLAN_APPROVAL_TIMEOUTnoabsolute 30-minute plan approval deadline expired
PLAN_APPROVAL_STALEnoresponse does not match the live proposal/session/turn/tool-call/version
PLAN_APPROVAL_INTERRUPTEDnopending approval closed during abort, crash, or persistence failure
PLAN_ARTIFACT_WRITE_FAILEDnohost could not write exact bytes to a new .pi/<kind>/*.md artifact
PLAN_EXECUTION_INTERRUPTEDnoapproved queued/running Plan or Goal execution stopped without replay
PLAN_REQUIRES_INTERACTIVE_SESSIONnounattended/scheduled Plan or Goal run cannot request approval

The _IN_PLAN suffix and the PLAN_ prefix are historical: both contract modes (Plan and Goal) share these codes rather than duplicating a _IN_GOAL set (D198). The renderer picks its wording from the proposal's kind, so one code can surface as either "Plan" or "Goal" copy.

3.4 Secrets / settings

coderetriablemeaning
PROVIDER_SECRET_MISSINGnoenabled provider requires an API key
SECRET_STORE_UNAVAILABLEmaybeOS secure storage unavailable (reserved)
SETTINGS_INVALIDnosettings payload invalid (reserved)

3.5 Plugins

coderetriablemeaning
PLUGIN_NOT_FOUNDnoplugin id missing (reserved)
PLUGIN_INVALIDnomanifest/package invalid
PLUGIN_LOAD_FAILEDmaybeenable/load failed
PLUGIN_DISABLEDnoplugin disabled (reserved)
PLUGIN_PERMISSION_DENIEDnoplugin lacks declared/granted permission (reserved)
PLUGIN_COMMAND_NOT_FOUNDnocommand id missing (reserved)
PLUGIN_CRASHEDyesplugin runtime crashed (reserved)
PLUGIN_CONTRACT_MISMATCHnounsupported manifest/api version (reserved)

3.6 Reserved detail codes (not yet emitted)

Finer-grained provider/tool distinctions documented for future mapping. Until emitted, implementations use the canonical parent code shown.

reserved codecanonical parent todaynotes
PROVIDER_BASE_URL_INVALIDPROVIDER_ERRORendpoint invalid (400)
PROVIDER_PROTOCOL_MISMATCHPROVIDER_ERRORwrong protocol profile
PROVIDER_MODEL_NOT_FOUNDMODEL_NOT_CONFIGUREDunknown model id (404)
PROVIDER_TIMEOUTTIMEOUTnetwork/server timeout (retriable)
PROVIDER_UNSUPPORTED_CAPABILITYPROVIDER_ERRORtools/vision unsupported
PROVIDER_DISABLEDMODEL_NOT_CONFIGUREDprovider disabled
WORKSPACE_PATH_DENIEDPATH_OUTSIDE_WORKSPACEignore/denylist block
TOOL_BINARY_CONTENTTOOL_FAILEDrefused binary dump

Historical aliases (never use in new code): PROVIDER_AUTH_FAILEDPROVIDER_UNAUTHORIZED; PROVIDER_STREAM_INTERRUPTEDSTREAM_FAILED; WORKSPACE_OUTSIDE_ROOTPATH_OUTSIDE_WORKSPACE; SECRET_MISSINGPROVIDER_SECRET_MISSING; SHELL_UNAVAILABLESHELL_NOT_FOUND; SHELL_IDENTITY_STALECOMMAND_SHELL_CHANGED; PLAN_APPROVAL_EXPIREDPLAN_APPROVAL_TIMEOUT. Truncation is not an error: a bounded tool result carries a marker naming which end survived and where the rest is, or reports the bounded window in sibling result fields (see 16-tool-result-limits).

4. Mapping rules

Host RPC numeric → AppError.code

See 06-host-rpc-protocol.md numeric table.
Example: host 1004TOOL_DENIED.

Provider exceptions

Node sidecar maps provider SDK errors into:

  • PROVIDER_UNAUTHORIZED
  • PROVIDER_RATE_LIMITED
  • MODEL_NOT_CONFIGURED (provider rejects the selected model with 404)
  • PROVIDER_ERROR
  • NETWORK_ERROR
  • STREAM_FAILED

An exact terminated provider message and equivalent premature stream-close messages map to STREAM_FAILED. A post-response transient failure may be retried once by the runtime; the second failure remains terminal.

Permission timeout

UI/host timeout emits PERMISSION_TIMEOUT internally, tool result presented as denied (TOOL_DENIED) to agent.

Shell and Plan/Goal checkpoint failures

SHELL_NOT_FOUND is returned only when catalog fallback finds no available platform shell. COMMAND_SHELL_CHANGED never retries with a different shell; the turn must obtain a fresh effective ID/dialect. PLAN_ARTIFACT_WRITE_FAILED never creates an approval row. PLAN_APPROVAL_TIMEOUT applies only to the absolute pending deadline; PLAN_EXECUTION_INTERRUPTED identifies an already-approved queued/running execution interrupted by abort or host recovery. PLAN_KIND_MISMATCH is a terminating tool error like PLAN_NOT_ACTIVE: the submit tool ran against the wrong contract, so no artifact is written and no approval row is created.

5. UI handling guidelines

classUI behavior
auth/config (PROVIDER_SECRET_MISSING, MODEL_NOT_CONFIGURED)assistant error message with settings CTA
permission denialsinline tool card state
retriable provider/networkassistant error message with retry action
internal/host unavailabledegraded banner + recovery tip

Message-bound provider failures never use a toast or floating global banner. The assistant error message shows a localized summary and stable code, with an accessible details disclosure containing the redacted provider response, provider ID, and model ID. Provider detail is capped at 600 characters and common credential/header values are redacted before event emission or persistence. When available, the details disclosure and timing logs may also show bounded phase, providerStatus, providerCode, providerWaitMs, streamMs, and retryAttempt fields.

6. i18n key convention

text
errors.<code>
errors.<code>.action

Examples:

  • errors.PROVIDER_SECRET_MISSING
  • errors.PROVIDER_SECRET_MISSING.action
  • errors.HOST_UNAVAILABLE

7. Acceptance

  1. Every IPC failure returns AppError.code
  2. No raw untyped string-only failures on main paths
  3. Plan/Goal hard-denies use explicit tool-specific codes; Bash is never denied by either contract mode solely because of the operating mode and instead follows permission policy
  4. Host numeric codes map to stable string codes
  5. Invalid shell settings, no-effective-shell/stale-pin, artifact-write, expiry, scheduled-rejection, and restart-interruption paths map to stable codes; only the documented pre-turn catalog fallback is allowed and no work is replayed

Built for local-first development.