Error Handling
Overview
Every error the SDK throws is a ChatSdkError with structured fields: a machine-readable code, severity level, retryability flag, a user-facing message safe to display in your UI, a developer hint for debugging, and a link to the error knowledge base. You never have to parse error strings.
ChatSdkError structure
import { ChatSdkError } from 'gecx-chat';
// Every ChatSdkError has these fields:
{
code: string; // e.g. 'TRANSPORT_CONNECT_FAILED'
message: string; // technical error message
severity: 'low' | 'medium' | 'high' | 'critical';
retryable: boolean; // true if the operation can be retried
userMessage: string; // safe to show in your UI
developerHint: string; // detailed debugging info for your logs
docsUrl: string; // link to the error knowledge base entry
requestId?: string; // request ID for support escalation
details?: Record<string, unknown>; // additional context
cause?: Error; // the underlying error, if any
}
Basic error handling
Catch ChatSdkError by checking instanceof:
import { ChatSdkError } from 'gecx-chat';
try {
await chat.sendText('Where is my order?');
} catch (error) {
if (error instanceof ChatSdkError) {
// Show the user-safe message in your UI
showToast(error.userMessage);
// Log the developer hint for debugging
console.error(`[${error.code}] ${error.developerHint}`);
// Retry if the error is retryable
if (error.retryable) {
scheduleRetry();
}
} else {
throw error; // re-throw non-SDK errors
}
}
Retryable vs non-retryable
Check error.retryable to decide whether to retry an operation.
Retryable errors are temporary. Network blips, server overload, interrupted streams. The SDK itself retries transport-level failures automatically with backoff, but tool-level and send-level retries are up to you.
TRANSPORT_CONNECT_FAILED-- connection failed, SDK is retryingSTREAM_INTERRUPTED-- stream dropped, SDK is reconnectingSEND_FAILED-- message send failedSERVER_TOOL_HTTP_FAILED-- server tool endpoint unreachableAUTH_EXPIRED-- token expired, SDK is refreshing
Non-retryable errors require a code change or user action. Retrying will produce the same result.
CONFIG_INVALID-- bad configurationAUTH_FAILED-- credentials rejectedTOOL_VALIDATION_FAILED-- schema mismatchTOOL_NOT_REGISTERED-- unknown toolSESSION_EXPIRED-- session is gone, create a new one
Error categories
| Prefix | What it covers |
|---|---|
CONFIG_* | Invalid or missing configuration |
AUTH_* | Authentication and token errors |
TOKEN_ENDPOINT_* | Token endpoint failures |
TRANSPORT_* | Connection and transport errors |
STREAM_* | SSE stream interruptions and parse errors |
SESSION_* | Session lifecycle (expired, ended, invalid state) |
SEND_* | Message send failures |
TOOL_* | Client tool execution errors |
SERVER_TOOL_* | Server tool HTTP and response errors |
NETWORK_* | Offline detection and reconnection |
GOVERNANCE_* | Data governance operations (delete, export, consent) |
Displaying errors in UI
Use error.userMessage for anything you show to users. It is written in plain language and never leaks internal details.
Use error.developerHint in your console logs and error tracking. It contains specific remediation steps.
// In a React component
if (chat.error) {
return (
<div role="alert">
<p>{chat.error.userMessage}</p>
{chat.error.retryable && (
<button onClick={() => chat.retry()}>Try again</button>
)}
</div>
);
}
Every error also has a docsUrl linking to its entry in the error knowledge base, which includes the full remediation steps.
The gecx doctor CLI
The bundled gecx doctor command diagnoses common integration errors before they reach production. Run it against your local token endpoint:
npx gecx doctor \
--token-endpoint http://localhost:3000/api/gecx-chat-token \
--deployment your-deployment-id
A typical run prints one line per check:
✓ token-endpoint reachable, returns { token, expiresAt, tokenType: 'Bearer' }
✓ deployment format ok
✗ csp connect-src missing wss://your-proxy.example.com → TRANSPORT_CONNECT_FAILED
✓ env .env.local present
Each ✗ failure resolves to a known ChatSdkError code with a docsUrl so you can jump straight to the fix. Use --help for the full flag list, or npx gecx verify to chain doctor with tsc --noEmit and your test suite into a single CI command. See commands reference for every CLI option.
Error categories at a glance
| Category | Code prefix | Where they originate |
|---|---|---|
| Config / auth | CONFIG_*, AUTH_*, TOKEN_* | Client-side, before any network call |
| Transport / stream | TRANSPORT_*, STREAM_* | Wire layer |
| Network / recovery | NETWORK_OFFLINE, RECONNECT_*, RESUME_*, LEADER_LOST, OUTBOX_DROPPED | Recovery FSM |
| Tools | TOOL_*, SERVER_TOOL_* | Tool dispatch |
| Sessions | SESSION_*, SEND_FAILED | Session FSM |
| Uploads | UPLOAD_*, FILE_VALIDATION_FAILED, URL_NOT_ALLOWED | Upload pipeline |
| Voice | VOICE_* (e.g. VOICE_TOKEN_ENDPOINT_MISSING, VOICE_PROVIDER_UNAVAILABLE) | Voice subsystem |
| Memory | MEMORY_* | Memory adapter and interceptor |
| Permissions | PERMISSION_DENIED, PERMISSION_DISMISSED, PERMISSION_INSECURE_CONTEXT, PERMISSION_PROMPT_BLOCKED, PERMISSION_UNSUPPORTED, PERMISSION_INVALIDATED | PermissionManager |
| Computer-use | COMPUTER_USE_* | Tool dispatch, proxy, session |
| Agent graph | A2A_*, AGENT_GRAPH_* | Graph runtime |
| Analytics | ANALYTICS_SINK_FAILED, ANALYTICS_AGGREGATION_INVALID_WINDOW | Analytics pipeline |
| Commerce / governance | COMMERCE_PII_LEAK | commerceGovernance strict mode |
| Handoff | HANDOFF_* | Handoff protocol FSM |
| A2UI | A2UI-specific codes — see error knowledge base | Surface manager, renderer, action bridge |
See the Error Knowledge Base for severity, retryability, and remediation per code.
What's next
- Error Knowledge Base -- every error code with full remediation
- Error Recovery Patterns -- recovery patterns by category with compilable React snippets
- Client Tools -- defining and registering browser-side tools
- Server Tools -- tools that execute on your backend
docs/guides/error-handling.md