Error handling

In shortEvery error class exported by the TypeScript client, its status and error code, when it is thrown, and how to handle it.

  • 8 min read
  • 10 sections
  • Updated
  • v0.10.0
  • Markdown

When the server returns a non-2xx response, TenxGraphClient throws an TenxGraphError or one of its 14 subclasses. The class is chosen from the response’s error code first, then from the HTTP status. Network failures and client timeouts are not API errors: they throw a plain Error.

Import: import { TenxGraphError } from '10xgraph-client';

For the practical guide (retries, stream errors), see Handle errors. For the codes the server can return, see Error codes.

How the client picks an error class

The client reads the JSON error body, then matches the error code by prefix, then falls back to the HTTP status. A GRAPH_RECURSION_ERROR returned with status 500 therefore arrives as GraphRecursionError, not ServerError.

Order Match Class
1 Code starts with GRAPH_RECURSION GraphRecursionError
2 Code starts with GRAPH GraphError
3 Code starts with NODE NodeError
4 Code starts with TRANSIENT_STORAGE TransientStorageError
5 Code starts with STORAGE StorageError
6 Code starts with METRICS MetricsError
7 Code starts with SCHEMA_VERSION SchemaVersionError
8 Code starts with SERIALIZATION SerializationError
9 Status 400, 401, 403, 404, 422 BadRequestError, AuthenticationError, PermissionError, NotFoundError, ValidationError
10 Status 500, 502, 503, 504 ServerError
11 Any other status TenxGraphError with code UNKNOWN_ERROR (unless the body gave one)

If the body is not JSON or cannot be parsed, the same status mapping applies with requestId set to 'unknown'.

TenxGraphError (base class)

TenxGraphError extends the built-in Error; every other class on this page extends it. Check it last in an instanceof chain, after the specific subclasses.

Property Type Description
message string Server error message, or the fallback message.
statusCode number HTTP status code.
errorCode string Machine-readable code, e.g. VALIDATION_ERROR.
requestId string metadata.request_id from the response, or 'unknown'.
timestamp string metadata.timestamp, or the client’s ISO 8601 time when missing.
details ErrorDetail[] Field-level details from the server. Empty array by default.
context Record<string, any> | undefined Extra context (see the note above).
endpoint string | undefined API path. Set only for unmapped status codes, and only if passed in.
method string | undefined HTTP method. Same condition as endpoint.
recoverySuggestion string | undefined Hint on how to fix the error. Set by the graph, node, storage, metrics, schema and serialization classes.

Two methods are available on every error:

Method Returns Description
getUserMessage() string message, plus "\n\nSuggestion: ..." when recoverySuggestion is set.
toJSON() Record<string, any> name, message, statusCode, errorCode, requestId, timestamp, details, context, endpoint, method, recoverySuggestion and stack.

toJSON() includes the stack trace, so log it on the server but do not send it to end users.

log-error.ts
import { TenxGraphClient, TenxGraphError, Message } from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

try {
  await client.invoke([Message.text_message('Hi')]);
} catch (err) {
  if (err instanceof TenxGraphError) {
    // Log structured details, show a safe message to the user.
    console.error(`Error ${err.statusCode} (${err.errorCode}): ${err.getUserMessage()}`);
    console.error(JSON.stringify(err.toJSON()));
  } else {
    throw err; // network failure or client-side timeout
  }
}

HTTP status errors

These five classes map to one status each. They carry no recovery suggestion, so read message and details.

Class Status Error code Thrown when
BadRequestError 400 BAD_REQUEST The server rejects the request as malformed.
AuthenticationError 401 AUTHENTICATION_FAILED Credentials are missing, invalid or expired.
PermissionError 403 PERMISSION_ERROR The caller is authenticated but not allowed to perform the action.
NotFoundError 404 RESOURCE_NOT_FOUND The thread, message, memory or file does not exist.
ValidationError 422 VALIDATION_ERROR The request body fails schema validation. details lists the failing fields.
http-status-errors.ts
import {
  TenxGraphClient,
  AuthenticationError,
  NotFoundError,
  ValidationError,
} from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

try {
  await client.threadDetails('thread-123');
} catch (err) {
  if (err instanceof AuthenticationError) {
    window.location.href = '/login'; // token missing or expired
  } else if (err instanceof NotFoundError) {
    console.error('Thread not found.');
  } else if (err instanceof ValidationError) {
    // Each detail is { loc?, msg?, type? }.
    for (const detail of err.details) {
      console.error(`Field ${detail.loc?.join('.')}: ${detail.msg}`);
    }
  } else {
    throw err;
  }
}

ServerError

ServerError covers status 500, 502, 503 and 504 when the error code does not match a more specific class. Its errorCode is the server’s code, or INTERNAL_SERVER_ERROR when absent, and statusCode is the actual status.

Treat 502, 503 and 504 as usually transient and retry them with backoff. Do not retry a 500 blindly: it is often a bug in a node or tool.

server-error.ts
import { TenxGraphClient, Message, ServerError } from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

try {
  await client.invoke([Message.text_message('Hi')]);
} catch (err) {
  if (err instanceof ServerError && [502, 503, 504].includes(err.statusCode)) {
    console.warn('Gateway or availability problem, retry later.');
  } else {
    throw err;
  }
}

Graph and node errors

These classes mean the graph failed while running. Each has status 500 and its own recovery suggestion.

Class Error code Extra property Recovery suggestion
GraphError GRAPH_ERROR none Check your graph configuration and ensure all nodes are properly connected.
NodeError NODE_ERROR nodeName?: string Review the node implementation and ensure all required inputs are provided.
GraphRecursionError GRAPH_RECURSION_ERROR recursionLimit?: number Consider increasing the recursion_limit parameter or check for infinite loops in your graph.

GraphRecursionError means a run exceeded its step limit. invoke sends recursion_limit (default 25) with each request, so raise it in the options if the graph legitimately needs more steps.

graph-errors.ts
import { TenxGraphClient, GraphRecursionError, Message, NodeError } from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

try {
  await client.invoke([Message.text_message('Plan my trip')], { recursion_limit: 50 });
} catch (err) {
  if (err instanceof GraphRecursionError) {
    console.error('Run exceeded its step limit. Check the graph for loops.');
  } else if (err instanceof NodeError) {
    console.error('A node failed:', err.message);
  } else {
    throw err;
  }
}

Storage errors

Storage errors mean the checkpointer or memory store failed. The two classes differ in whether a retry helps.

Class Status Error code Retry Recovery suggestion
StorageError 500 STORAGE_ERROR No Check your storage configuration and ensure the storage backend is accessible.
TransientStorageError 503 TRANSIENT_STORAGE_ERROR Yes, with backoff This is a temporary issue. Please retry your request after a short delay.

TransientStorageError is matched before StorageError because its code starts with TRANSIENT_STORAGE.

storage-errors.ts
import { TenxGraphClient, StorageError, TransientStorageError } from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

async function loadState(threadId: string) {
  try {
    return await client.threadState(threadId);
  } catch (err) {
    if (err instanceof TransientStorageError) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      return client.threadState(threadId); // one retry after a short delay
    }
    if (err instanceof StorageError) {
      console.error('Storage backend is unavailable:', err.message);
    }
    throw err;
  }
}

Serialization, schema and metrics errors

These three classes cover data format, version and telemetry failures.

Class Status Error code Thrown when Recovery suggestion
SerializationError 500 SERIALIZATION_ERROR A payload cannot be serialized or deserialized. Ensure your data format is compatible with the API schema.
SchemaVersionError 422 SCHEMA_VERSION_ERROR Client and server schema versions disagree. Has expectedVersion? and actualVersion? (string). Update your client to match the server schema version or contact support.
MetricsError 500 METRICS_ERROR Metrics collection or reporting failed. Check your metrics configuration. This error typically doesn’t affect core functionality.

SchemaVersionError shares status 422 with ValidationError; the code prefix is what separates them. A MetricsError may mean the operation itself worked, so log it and decide per call whether to treat it as fatal.

schema-errors.ts
import { TenxGraphClient, MetricsError, SchemaVersionError } from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });

try {
  await client.updateThreadState('thread-1', {}, { context: [] });
} catch (err) {
  if (err instanceof SchemaVersionError) {
    console.error('Client and server schemas differ. Update the client.');
  } else if (err instanceof MetricsError) {
    console.warn('Metrics failed; the request may have succeeded.');
  } else {
    throw err;
  }
}

A complete recovery strategy

Check specific subclasses first and the base class last. This handler routes each failure to the right response: log in again, show field errors, retry once, or surface a safe message.

handle-errors.ts
import {
  TenxGraphClient,
  TenxGraphError,
  AuthenticationError,
  GraphRecursionError,
  Message,
  TransientStorageError,
  ValidationError,
} from '10xgraph-client';

const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function ask(text: string, attempt = 0): Promise<unknown> {
  try {
    return await client.invoke([Message.text_message(text)]);
  } catch (err) {
    if (err instanceof AuthenticationError) {
      window.location.href = '/login';
    } else if (err instanceof ValidationError) {
      console.error('Invalid request:', err.details);
    } else if (err instanceof TransientStorageError && attempt < 3) {
      await sleep(1000 * 2 ** attempt); // exponential backoff
      return ask(text, attempt + 1);
    } else if (err instanceof GraphRecursionError) {
      console.error('Step limit exceeded. Raise recursion_limit or fix the loop.');
    } else if (err instanceof TenxGraphError) {
      console.error(err.getUserMessage());
    } else {
      throw err; // network error or timeout
    }
  }
}

await ask('Hello');

Build errors in your own proxy

parseErrorResponse and createErrorFromResponse are exported for code that calls the API itself, for example a server route that proxies requests. Use them to get the same error classes the client produces.

Function Parameters Returns
parseErrorResponse response: Response Promise<ApiErrorResponse | null>. null if the content type is not JSON or parsing fails.
createErrorFromResponse response: Response, fallbackMessage?: string, endpoint?: string, method?: string Promise<TenxGraphError>, the most specific subclass.
app/api/invoke/route.ts
import { createErrorFromResponse } from '10xgraph-client';

export async function POST(req: Request) {
  const upstream = await fetch('http://localhost:8000/v1/graph/invoke', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: await req.text(),
  });

  if (!upstream.ok) {
    const error = await createErrorFromResponse(
      upstream,
      'Invoke failed',
      '/v1/graph/invoke',
      'POST'
    );
    // Return a safe subset. toJSON() includes the stack trace.
    return Response.json(
      { code: error.errorCode, message: error.message, requestId: error.requestId },
      { status: error.statusCode }
    );
  }

  return Response.json(await upstream.json());
}

Response shapes

These exported types describe the error body the server sends and the field-level detail entries.

TypeScript
interface ErrorDetail {
  loc?: string[]; // field path, e.g. ['body', 'messages']
  msg?: string;   // human-readable message
  type?: string;  // failure type, e.g. 'value_error'
}

interface ApiErrorResponse {
  metadata: { message: string; request_id: string; timestamp: string };
  error: { code: string; message: string; details: ErrorDetail[] };
}
Last updated for v0.10.0Edit this page on GitHubReport an issue