Error Codes
Every ChatSdkError carries a typed code, userMessage, developerHint, severity, retryability flag, and a docsUrl linking to this page. Catch errors by checking instanceof ChatSdkError and branching on error.code.
import { ChatSdkError } from 'gecx-chat';
try {
await session.send('Hello');
} catch (err) {
if (err instanceof ChatSdkError) {
console.log(err.code, err.userMessage, err.developerHint);
if (err.retryable) scheduleRetry();
}
}
Categories
For category-level recovery patterns (auth retry loops, transport reconnect strategies, upload backoff, etc.) see the Error Recovery guide.
Authentication
| Code | Severity | Retryable | Description | Fix |
|---|
AUTH_REQUIRED | high | No | No auth provider was supplied. | Pass tokenEndpointAuth(), customAuth(), or googleTokenBrokerAuth() to createChatClient. |
AUTH_EXPIRED | high | Yes | Auth token past its expiresAt. SDK will attempt auto-refresh. | If recurring, lengthen token TTL or shorten prefetchBufferMs. Ensure Cache-Control: no-store on the token endpoint. |
AUTH_FAILED | high | No | Server rejected auth credentials. | Verify the token endpoint returns a valid token field. Check broker URL for googleTokenBrokerAuth. |
TOKEN_ENDPOINT_FAILED | high | Yes | Token endpoint request failed (network or non-2xx). | Verify endpoint reachability and CORS. Run npx gecx doctor --token-endpoint <url>. |
TOKEN_ENDPOINT_INVALID_RESPONSE | high | No | Token endpoint returned unparseable response. | Return JSON as { "token": "...", "expiresAt": <ms>, "tokenType": "Bearer" }. |
Configuration
| Code | Severity | Retryable | Description | Fix |
|---|
CONFIG_INVALID | medium | No | Required config fields missing or malformed. | Run client.validateConfig() and inspect errors. See schemas/config.schema.json. |
Transport
| Code | Severity | Retryable | Description | Fix |
|---|
TRANSPORT_UNAVAILABLE | medium | No | No transport could be initialized. | Pass a valid ChatTransport to createChatClient. |
TRANSPORT_CONNECT_FAILED | medium | Yes | Transport connection failed. | Check endpoint reachability, CORS, CSP connect-src. SDK retries with backoff. |
STREAM_INTERRUPTED | medium | Yes | Event stream closed unexpectedly. | Set X-Accel-Buffering: no on proxies. SDK reconnects automatically. |
STREAM_PARSE_ERROR | medium | No | Failed to parse an SSE event. | Ensure upstream emits valid data: { ... } frames matching TransportEvent. |
Session
| Code | Severity | Retryable | Description | Fix |
|---|
SESSION_EXPIRED | medium | No | Session ID no longer valid on backend. | Create a new session with client.createSession(). |
SESSION_ENDED | medium | No | Session explicitly ended. | Create a new session to continue. |
SESSION_INVALID_STATE | medium | No | Invalid state transition attempted. | Wait for status === 'ready' before calling send. |
SEND_FAILED | medium | Yes | Send request failed before response stream began. | Usually transient. SDK retries when retry.maxAttempts > 1. |
| Code | Severity | Retryable | Description | Fix |
|---|
TOOL_NOT_REGISTERED | medium | No | Server invoked an unregistered tool. | Register all expected tools before creating the session. |
TOOL_VALIDATION_FAILED | low | No | Tool input failed schema validation. | Compare payload to the tool's inputSchema. |
TOOL_EXECUTION_FAILED | medium | No | Tool handler threw during execution. | Add error handling inside the tool's execute function. |
TOOL_TIMEOUT | medium | No | Tool handler exceeded timeoutMs. | Increase timeoutMs or optimize the handler. |
| Code | Severity | Retryable | Description | Fix |
|---|
SERVER_TOOL_NOT_CONFIGURED | high | No | Server tool endpoint missing or proxy returned PROXY_NOT_CONFIGURED. | Set http.endpoint on defineServerTool and configure the proxy. |
SERVER_TOOL_HTTP_FAILED | medium | Yes | HTTP call to tool endpoint failed. | Check proxy audit log. Confirm endpoint is reachable. |
SERVER_TOOL_INVALID_RESPONSE | medium | No | Tool endpoint returned unparseable response. | Return ServerToolResultEnvelope: { "status": "completed", "output": {...} } with Content-Type: application/json. |
SERVER_TOOL_UNAUTHORIZED | high | No | Proxy/backend rejected the request with 401/403. | Refresh the chat token. Verify origin is in ALLOWED_ORIGINS. |
Files
| Code | Severity | Retryable | Description | Fix |
|---|
FILE_VALIDATION_FAILED | low | No | File failed pre-upload validation (size, type, or count). | Adjust allowedFileTypes, maxFileSize, or maxFileCount. |
UPLOAD_FAILED | medium | Yes | Upload request failed or the transport is unsupported. | Use mock/proxy upload, configure uploadEndpoint, or wait for a validated inline blob contract on live session transports. |
URL_NOT_ALLOWED | low | No | URL failed the allowlist check. | Add the trusted host to urlAllowlist in rich content registry options. |
A2UI (Agent-to-UI)
| Code | Severity | Retryable | Description | Fix |
|---|
A2UI_FRAME_INVALID | medium | No | Malformed A2UI frame received. | Validate frame uses v0.9 and a single valid operation. |
A2UI_VERSION_UNSUPPORTED | high | No | Agent sent A2UI for an unsupported protocol version. | Emit version: 'v0.9' from the agent. |
A2UI_CATALOG_REJECTED | medium | No | Agent referenced an unregistered catalog. | Register the catalog with createA2UIRenderer. |
A2UI_ACTION_INVALID | medium | No | A2UI action envelope missing required fields. | Ensure action includes a string name and is dispatched from an initialized surface. |
A2UI_ACTION_REJECTED | low | No | A2UI action policy blocked the action. | Check action against the host policy. Use confirm instead of block when appropriate. |
A2UI_RENDER_FAILED | medium | Yes | React A2UI renderer threw during render. | Inspect catalog, component payload, and renderer error boundary. |
Network and Recovery
| Code | Severity | Retryable | Description | Fix |
|---|
NETWORK_OFFLINE | low | Yes | navigator.onLine === false. | In auto mode, sends queue in the outbox. In strict mode, surface the error to the user. |
RECONNECT_EXHAUSTED | high | No | Max reconnect attempts reached. | Increase recovery.maxAttempts or recovery.maxBackoffMs. |
RESUME_NOT_SUPPORTED | medium | No | Resume mode requested but transport does not support it. | Downgrade recovery.resumeMode to 'replay' or use a transport with capabilities.resume: true. |
LEADER_LOST | low | No | Multi-tab leader lock released unexpectedly. | Usually self-heals. A follower is promoted automatically. |
OUTBOX_DROPPED | medium | No | Pending offline sends lost on shutdown. | Persist the outbox or drain before shutdown. |
IDEMPOTENCY_KEY_REJECTED | medium | Yes | Upstream rejected the idempotency key. | Generate a fresh key with generateIdempotencyKey(). |
Identity
| Code | Severity | Retryable | Description | Fix |
|---|
IDENTITY_REQUIRED | medium | No | upgradeToAuthenticated() called without externalId. | Pass a non-empty externalId. |
IDENTITY_CONFLICT | low | Yes | Another tab wrote a different identity. | Informational. Centralize identity in a single host UI. |
CONVERSATION_NOT_FOUND | medium | No | resumeSession called with unknown conversation ID. | Call client.conversations.importRemote() first for cross-device resume. |
Governance
| Code | Severity | Retryable | Description | Fix |
|---|
GOVERNANCE_SESSION_ERASED | medium | No | Send attempted on a deleted session. | Create a new session with client.createSession(). |
GOVERNANCE_CONSENT_WITHDRAWN | medium | No | Consent is withdrawn and policy is clear_and_block. | Call governance.updateConsent() to restore consent, or change policy to clear_only. |
GOVERNANCE_DELETE_FAILED | medium | Yes | Backend delete/forget failed. | Verify proxy routes. Set requireServerAck.delete = false for local-only erasure. |
GOVERNANCE_EXPORT_FAILED | medium | Yes | Backend export failed. | Set policy.export.localFallback = true or implement the export endpoint on the proxy. |
GOVERNANCE_NOT_SUPPORTED | medium | No | Transport missing required governance verb. | Add the verb to the transport or relax requireServerAck. |
Rich Content
| Code | Severity | Retryable | Description | Fix |
|---|
RICH_CONTENT_VERSION_UNSUPPORTED | low | No | Server sent a rich part version this client does not render. | Register a renderer for the new version on the RichContentRegistry. |
RICH_CONTENT_FAMILY_UNREGISTERED | low | No | Server sent a rich part family not in the registry. | Register the family: registry.register({ family: '...', versions: [1] }). |
Voice
| Code | Severity | Retryable | Description | Fix |
|---|
VOICE_CONSENT_REQUIRED | medium | No | Audio retention requested without voice_recording consent. | Grant voice_recording via the governance policy, or open the VoiceSession with retention disabled (the default). |
VOICE_RECORDING_DENIED | low | No | User declined the voice recording consent prompt. | Continue without retention (transcripts only). Do not re-prompt within the same session. |
VOICE_PROVIDER_UNAVAILABLE | high | Yes | The configured VoiceProvider failed to open a session. | For Gemini Live, verify /chat/voice-token is wired and reachable. For the WebRTC stub, implement an OpenAI Realtime or LiveKit-partner adapter. |
VOICE_DEVICE_PERMISSION_DENIED | medium | No | Browser rejected getUserMedia({ audio: true }). | Serve over HTTPS, check Permissions-Policy: microphone=*, and prompt the user to allow microphone access. |
VOICE_DEVICE_UNAVAILABLE | medium | No | No audio input devices found, or device removed mid-session. | Prompt the user to connect a microphone. On mobile Safari, ensure the tab is foregrounded. |
VOICE_NEGOTIATION_FAILED | high | Yes | WebRTC SDP exchange failed or audio MIME negotiation rejected the offered formats. | Confirm the provider supports audio/pcm;rate=16000 input and audio/pcm;rate=24000 output. Verify TURN reachability on enterprise networks. |
VOICE_BARGE_IN_TIMEOUT | low | Yes | Barge-in abort did not propagate within 1s. | Check the provider stream loop and network path. Telemetry-only; the SDK still cancels the turn locally. |
VOICE_TOKEN_INVALID | high | Yes | Ephemeral voice token was rejected by the provider. | Verify /chat/voice-token mints tokens with the correct model + voice scopes and that clock skew is within tolerance. |
VOICE_TOKEN_ENDPOINT_MISSING | high | No | voice is configured for the bundled Gemini Live provider but no tokenEndpoint was supplied in production/staging. | Add voice.tokenEndpoint pointing to your proxy /chat/voice-token route, or use voice: 'auto' to fall back to the documented default. |
Memory
| Code | Severity | Retryable | Description | Fix |
|---|
MEMORY_ADAPTER_UNAVAILABLE | low | Yes | The configured MemoryAdapter rejected a request — the remote backend may be down or unreachable. Sends still proceed; only the memory layer is degraded. | Check adapter connectivity and credentials. The hybrid adapter will keep serving reads from the cache until the remote recovers. |
MEMORY_CONSENT_WITHDRAWN | low | No | A memory operation was attempted while governance consent is withdrawn or useMemory().setEnabled(false) was set. | Re-enable memory via the user-controllable kill switch (useMemory().setEnabled(true)) or restore governance consent. Existing entries are preserved on disk for the duration governed by retention policy. |
MEMORY_QUOTA_EXCEEDED | low | No | The per-scope or total-bytes quota in MemoryConfig.quota was reached. | Raise the quota, prune old entries with memory.delete() / clear(), or rely on retention TTLs to clean up automatically. |
MEMORY_SYNC_FAILED | low | Yes | The hybrid adapter could not reconcile a local optimistic write against the remote source-of-truth. | Inspect the remote endpoint logs. The local cache was rolled back per HybridMemoryAdapterOptions.onRemoteFailure. |
MEMORY_EXTRACTION_FAILED | low | Yes | The configured MemoryExtractor threw or returned invalid output. | The send already completed — this is a soft failure that drops the extraction for one turn. Check extractor configuration and the upstream model output. |
MEMORY_INVALID_SCOPE | medium | No | A memory operation referenced a scope that does not match the active identity, or omitted required scope fields. | Verify the IdentityManager is hydrated and that the operation includes a valid identityId. |
MEMORY_ENTRY_NOT_FOUND | low | No | memory.update / memory.delete was invoked with an id that does not exist in the adapter. | Refresh the list with useMemory() and retry. The entry may have already been deleted or expired via retention. |
Device permissions: VOICE_DEVICE_PERMISSION_DENIED and VOICE_RECORDING_DENIED predate the first-party permissions module. Cross-platform code should branch on PERMISSION_* codes thrown by PermissionManager.ensure() — see the Permissions section below.
Permissions
Cross-platform device-permission errors raised by PermissionManager.ensure() and UploadManager.attachFromCamera/Microphone/Screen. The data-only request() form never throws — it resolves with { status, reason } instead. The throwing surface here is the convenience used by higher-level call sites (VoiceSession.beginInput, attachFromCamera).
| Code | Severity | Retryable | Description | Fix |
|---|
PERMISSION_DENIED | medium | No | User dismissed or rejected the OS / browser permission prompt. | Inspect details.capability and details.reason. Surface rationale UI and let the user opt back in deliberately — do not re-prompt automatically. |
PERMISSION_BLOCKED | medium | No | Platform reports the capability is permanently blocked (native: app-level denial). | Render an "open settings" recovery path. On web there is no programmatic deep link — show instructions. Browsers cannot reliably distinguish blocked from per-session denial. |
PERMISSION_UNSUPPORTED | medium | No | The configured PermissionProvider reports the capability is unavailable. | Common causes: SSR, non-DOM web worker, missing platform plugin (RN/Capacitor), or navigator.mediaDevices is undefined. Insecure context is signalled separately as PERMISSION_INSECURE_CONTEXT. |
PERMISSION_INSECURE_CONTEXT | medium | No | Browser refused to expose getUserMedia / getDisplayMedia / geolocation because window.isSecureContext is false. | Serve the page over HTTPS or use localhost during development. The SDK surfaces this distinct from PERMISSION_UNSUPPORTED so the host can render a setup-guidance message. |
PERMISSION_TIMEOUT | low | Yes | User did not resolve the prompt within the host-supplied timeout, or geolocation hit its own timeout. | Offer a retry affordance — most users grant on a second try after re-reading rationale copy. |
PERMISSION_HARDWARE_UNAVAILABLE | medium | Yes | Platform returned NotFoundError / NotReadableError / OverconstrainedError, or geolocation reported POSITION_UNAVAILABLE. | The capability is supported but the underlying device is missing, busy, or rejected the constraints. Prompt the user to plug in / unplug peripherals; relax constraints (resolution, facingMode) before failing hard. |
Computer-use
The computer_use server tool is default off for security. Errors below surface either before any vendor session is allocated (fail-fast SDK guard) or while a live session is mid-flight (proxy-enforced). Every transition is also recorded as a governance.computer_use.* audit event.
| Code | Severity | Retryable | Description | Fix |
|---|
COMPUTER_USE_NOT_ENABLED | medium | No | governance.computerUse.enabled is false (the default) or the proxy is missing COMPUTER_USE_PROVIDER. | Set governance.computerUse to a populated policy (enabled: true, allowlist: [...]) and configure the proxy provider env vars. |
COMPUTER_USE_ALLOWLIST_VIOLATION | high | No | A requested URL was not in governance.computerUse.allowlist. | Never widen the SDK-side allowlist to suppress this; fix the upstream agent prompt or extend the policy intentionally after a security review. |
COMPUTER_USE_DURATION_EXCEEDED | medium | No | Session exceeded maxDurationMs (default 5 minutes). | Raise the cap deliberately or have the agent break the goal into smaller actions. |
COMPUTER_USE_ACTION_LIMIT_EXCEEDED | medium | No | Session exceeded maxActionsPerSession (default 30). | Inspect the governance.computer_use.action audit events for a loop pattern; tighten the agent's plan or raise the cap deliberately. |
COMPUTER_USE_USER_ABORT | low | No | The user clicked Abort on the <ComputerUseSurface>. | This is not an error — surface it as a normal cancellation in product UX. |
COMPUTER_USE_PROVIDER_UNAVAILABLE | high | Yes | The provider (Browserbase or Mock) failed to open a session, or the kill-switch is engaged. | For Browserbase, check BROWSERBASE_API_KEY / BROWSERBASE_PROJECT_ID and the vendor status page. For the kill-switch, flip COMPUTER_USE_KILL_SWITCH or governance.triggerComputerUseKill(false) once safe. |
Agent graph
| Code | Severity | Retryable | Description | Fix |
|---|
A2A_TRANSPORT_ERROR | medium | Yes | The A2A JSON-RPC call to a specialist agent failed (network error, non-2xx, unparseable body, or SSE error frame). | Check the specialist endpoint, AgentCard URL, and any auth headers. The router falls back to its declared fallback when the in-flight call fails. |
A2A_AGENT_UNAVAILABLE | medium | Yes | The A2A endpoint returned a 5xx or failed-state task. | The router will surface the configured fallback for this turn; if no fallback is configured, the turn errors with this code. Restore the specialist or escalate via routeTo: 'human'. |
A2A_TIMEOUT | medium | Yes | The A2A request exceeded the configured per-specialist timeoutMs (default 8s). | Increase A2AAgentClientOptions.timeoutMs for the specialist or examine the specialist for slow handlers. |
A2A_AGENT_CARD_INVALID | high | No | The AgentCard fetched from the specialist did not match the expected A2A schema, or used an unsupported protocolVersion. | Verify the .well-known/agent.json document is valid JSON with name, protocolVersion, and url. The SDK supports A2A 0.x. |
AGENT_GRAPH_INVALID | high | No | agentGraph() rejected the supplied definition (duplicate node ids, missing entry node, or a router rule whose routeTo does not resolve). | Inspect the error details.cause and fix the configuration before constructing the chat client. |
AGENT_GRAPH_NODE_NOT_FOUND | high | No | A router decision targeted a node id that is not registered in the graph. | Typically a typo in a routeTo string. The error includes details.knownNodes listing the valid ids. |
AGENT_GRAPH_MAX_DEPTH | high | No | A turn caused the router to recurse beyond AgentGraphConfig.maxDepth (default 3). Usually a routing loop where two routers hand off to each other. | Add a base-case rule that breaks the loop, or raise maxDepth if your graph is genuinely deeper than three router hops. |
Analytics
| Code | Severity | Retryable | Description | Fix |
|---|
ANALYTICS_SINK_FAILED | low | Yes | A ProductAnalyticsSink threw or its promise rejected. The collector swallows sink errors by default — this code only surfaces when you wrap a sink with strict error propagation. | Inspect the sink's stack trace. Sinks must never break the SDK, so consider wrapping with try/catch and rate-limiting any logging to avoid noisy retries. |
ANALYTICS_AGGREGATION_INVALID_WINDOW | medium | No | computeDeflectionRate / computeCsat / etc. received an invalid TimeRangeFilter: both range and window set, a zero-length window, or a negative duration. | Pass exactly one of range (preset like '7d') or window (explicit { startMs, endMs }). The window must be half-open with endMs > startMs. |
Commerce
| Code | Severity | Retryable | Description | Fix |
|---|
COMMERCE_PII_LEAK | critical | No | commerceGovernance({ strict: true }) detected an unredacted PAN or PII pattern in a tool input, rich-content part, transcript, or debug bundle at the session-state-entry boundary. | Apply redactCommercePii() or redactCommercePiiDeep() at the boundary before persisting, emitting analytics, or surfacing in approval text. See commerce.md. |
| Code | Severity | Retryable | Description | Fix |
|---|
AGENTIS_SESSION_INIT_FAILED | high | Yes | The customer session route could not complete Agentis :initializeSession, or the response was malformed. | Keep :initializeSession server-side through createAgentisSessionHandler, confirm OAuth impersonation, and check route logs. See agentis-integration.md. |
AGENTIS_TENANT_NOT_ALLOWLISTED | high | No | Agentis returned 404 despite valid credentials. The numeric GCP project is not allowlisted in TenantManager. | Ask your Google FDE to apply the COMMERCE_PARTNER label. Verify agenticapplications.googleapis.com, AGENTIS_AGENT_ID, and AGENTIS_CLIENT_ID. |
AGENTIS_AUTH_PRINCIPAL_REJECTED | high | No | Agentis rejected the OAuth principal with 401/403. | Confirm the server-side token broker uses service-account impersonation with the cloud-platform scope. API keys and end-user OAuth tokens cannot initialize Agentis sessions. |
AGENTIS_RECAPTCHA_REQUIRED | medium | Yes | The server-side reCAPTCHA hook required or rejected verification. | Pass the customer route's expected reCAPTCHA token and verify it server-side before session allocation. |
AGENTIS_SHOPPING_EVENT_FAILED | low | Yes | The customer shopping-event relay could not deliver :createShoppingEvent. | Verify AGENTIS_PROJECT_NUMBER, OAuth impersonation, relay CORS, and the { parent, shoppingEvent } envelope. |
Voice (additional)
| Code | Severity | Retryable | Description | Fix |
|---|
VOICE_TOKEN_ENDPOINT_MISSING | high | No | An explicit geminiLive voice provider was configured without a tokenEndpoint. Surfaced by validateConfig() in production/staging. | Pass tokenEndpoint: '/api/chat/voice-token' (or your proxy's path) to createGeminiLiveProvider. The proxy reference implements this route. |
Internal
| Code | Severity | Retryable | Description | Fix |
|---|
INTERNAL_ERROR | critical | No | Unexpected SDK bug. | Capture a debug bundle and file an issue with requestId and correlationId. |
Source: docs/reference/error-codes.md