Error handling
In shortCatch, classify, and recover from errors in the TypeScript client. Error classes, retrying transient failures, and streaming error events.
- 5 min read
- 8 sections
- Updated
- v0.10.0
- Markdown
Every API call from the client can fail: the server can reject authentication, return validation errors, encounter internal issues, or experience temporary storage outages. The 10xGraph TypeScript client exports a hierarchy of error classes to help you classify failures and respond appropriately.
Error hierarchy
All errors inherit from TenxGraphError, which gives you structured access to the status code, error code, request ID, and recovery suggestions.
import {
TenxGraphError,
BadRequestError,
AuthenticationError,
PermissionError,
NotFoundError,
ValidationError,
ServerError,
GraphError,
NodeError,
StorageError,
TransientStorageError,
} from '10xgraph-client';| Error Class | HTTP Status | Meaning | Recovery |
|---|---|---|---|
BadRequestError |
400 | Malformed request or invalid data | Fix the request and retry immediately |
AuthenticationError |
401 | Missing or invalid auth credentials | Check token, refresh if expired, retry |
PermissionError |
403 | User lacks access to the resource | Check authorization scope, request new permissions |
NotFoundError |
404 | Resource (thread, file) does not exist | Verify the ID, create the resource, or move on |
ValidationError |
422 | Request failed schema validation | Fix field types and ranges, retry |
ServerError |
500, 502, 503, 504 | Server-side failure or transient issue | Retry with exponential backoff |
GraphError |
500 | Error during graph execution | Check graph configuration, inspect logs |
NodeError |
500 | Error in a specific graph node | Review node implementation and inputs |
StorageError |
500 | Checkpointer or memory store is unavailable | Verify storage backend is online, retry |
TransientStorageError |
503 | Temporary storage issue | Retry immediately or after brief delay |
The client automatically instantiates the appropriate error class based on HTTP status code and error code from the server.
Catching errors
Wrap invoke() or stream() in a try-catch block:
import { TenxGraphClient, Message, TenxGraphError, TransientStorageError } from '10xgraph-client';
const client = new TenxGraphClient({
baseUrl: 'http://localhost:8000',
authToken: process.env.TENXGRAPH_TOKEN,
});
try {
const result = await client.invoke(
[Message.text_message('Hello', 'user')],
{ config: { thread_id: 'user-123' } }
);
console.log('Response:', result.messages);
} catch (err) {
if (err instanceof TransientStorageError) {
console.log('Storage temporarily unavailable. Try again in a moment.');
} else if (err instanceof TenxGraphError) {
console.error(`Request failed: ${err.message}`);
console.error(`Request ID: ${err.requestId}`);
console.error(`Status: ${err.statusCode}`);
} else {
// Network or other non-API error
console.error('Unexpected error:', err);
}
}Using getUserMessage and toJSON
TenxGraphError provides two utility methods for displaying and debugging errors.
getUserMessage() returns a user-friendly message that includes a recovery suggestion if one is available:
try {
await client.invoke([Message.text_message('Hello', 'user')]);
} catch (err) {
if (err instanceof TenxGraphError) {
// Suitable for displaying to an end user
console.log(err.getUserMessage());
// Output: "Invoke request failed\n\nSuggestion: Check your graph configuration..."
}
}toJSON() returns a detailed object with all error fields for logging or debugging. Use this when reporting errors to a monitoring system:
if (err instanceof TenxGraphError) {
console.log(JSON.stringify(err.toJSON(), null, 2));
// {
// "name": "GraphError",
// "message": "Node 'search_tool' failed",
// "statusCode": 500,
// "errorCode": "GRAPH_ERROR",
// "requestId": "req-abc123...",
// "timestamp": "2026-10-08T14:32:01.234Z",
// "details": [...],
// "recoverySuggestion": "Check your graph configuration...",
// "stack": "..."
// }
}Retrying transient errors
Some errors are temporary and safe to retry. The client itself does not retry automatically, but you can implement retry logic in your application.
Transient errors are those where the problem is temporary and will likely resolve on its own:
TransientStorageError(503): the checkpointer or memory store is momentarily unavailableServerErrorwith status 502/504: a gateway or upstream service is temporarily down- Network timeouts or connection resets (a plain
Error, such asRequest timeout after 300000ms, not aTenxGraphError)
Non-transient errors should not be retried the same way:
AuthenticationError(401): fix your token before retryingValidationError(422): fix your request before retryingNotFoundError(404): the resource does not exist; create it firstPermissionError(403): request new permissions before retrying
Here’s a helper function for exponential backoff retry:
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxAttempts: number = 3,
baseDelayMs: number = 500
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
const isTransient =
err instanceof TransientStorageError ||
(err instanceof ServerError && [502, 504].includes(err.statusCode));
if (!isTransient || attempt === maxAttempts) {
throw err; // Re-throw non-transient errors and final attempt
}
const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
console.log(`Attempt ${attempt} failed. Retrying in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
}
// Usage
const result = await retryWithBackoff(() =>
client.invoke([Message.text_message('Query', 'user')], {
config: { thread_id: 'user-123' },
})
);Streaming errors
When you call stream(), errors appear as part of the stream event sequence, not as thrown exceptions. The stream is NDJSON: one JSON object per line, and each object is a StreamChunk.
export enum StreamEventType {
MESSAGE = 'message',
UPDATES = 'updates',
STATE = 'state',
ERROR = 'error',
}Iterate through the stream and check the event field. When an error occurs, the server sends a chunk with event: 'error':
const stream = client.stream([Message.text_message('Hello', 'user')], {
config: { thread_id: 'user-123' },
});
try {
for await (const chunk of stream) {
if (chunk.event === 'error') {
// Error in the stream
console.error('Stream error:', chunk.data);
// chunk.data carries the failure reason, for example:
// { "reason": "..." }
break; // Stop processing stream
} else if (chunk.event === 'message') {
console.log('Message:', chunk.message);
} else if (chunk.event === 'updates') {
console.log('State:', chunk.state);
}
}
} catch (err) {
// Network errors or stream parsing errors
if (err instanceof TenxGraphError) {
console.error('Request failed:', err.message);
} else {
console.error('Stream error:', err);
}
}Graph and node errors
When a graph node raises an exception or a tool fails, the error is captured by the server and returned as GraphError or NodeError. These errors are returned as HTTP 500 responses. The error message describes which node failed and why.
try {
const result = await client.invoke([Message.text_message('Search for X', 'user')], {
config: { thread_id: 'user-123' },
});
} catch (err) {
if (err instanceof NodeError) {
console.error('Node failed:', err.message);
} else if (err instanceof GraphError) {
console.error('Graph execution failed:', err.message);
}
}Debugging and logging
Set debug: true in the client config to enable detailed logging:
const client = new TenxGraphClient({
baseUrl: 'http://localhost:8000',
debug: true, // Logs every request, response, and chunk
});In logs, look for:
request_idin every error: use this to correlate client logs with server logstimestamp: when the error occurred on the serverdetails: validation errors or field-specific issuesrecoverySuggestion: guidance on what to do next
When reporting errors to your team or to 10xGraph support, include the full error JSON from toJSON() and the request ID.