Errors
The ErrorInfo type that every AI Transport failure path surfaces, the numeric codes you match against, and the patterns for handling errors on the client and the agent.
Every rejected promise and every on('error') event carries an ErrorInfo object. The same type covers AI Transport-specific failures, Ably platform errors (auth, channel attach, rate limits), and underlying network errors. Match against a specific code with the errorInfoIs utility.
ErrorInfo
The ErrorInfo type is exported from ably. AI Transport surfaces it as the rejection reason on every promise that can fail, and as the argument to every on('error') listener.
Properties
codeNumbererrorInfoIs to identify a specific failure.messageStringstatusCodeNumbercauseErrorInfo or UndefineddetailRecord<string, string> or UndefinedAI Transport error codes
General errors
| Code | Name | HTTP status | Description | Recovery |
|---|---|---|---|---|
| 40000 | BadRequest | 400 | The request was malformed or missing required fields. | Check the request payload and ensure all required fields are present. |
| 40003 | InvalidArgument | 400 | An argument passed to a session or run method was invalid. | Verify the arguments match the expected types and constraints. |
| 40033 | OperationCancelled | 400 | The operation was cancelled. The run was cancelled, the caller's abort signal fired, or the session began closing while the operation was in flight. | Expected during a deliberate cancel. Treat it as a normal outcome rather than a failure to retry. |
| 40160 | InsufficientCapability | 401 | The Ably channel rejected a publish for a capability reason. The token does not permit the operation. | Add the missing capability to the token. |
| 50000 | InternalError | 500 | An internal invariant failed, so the SDK cannot explain or recover from what it observed. | Retry the operation. |
Session and run errors
| Code | Name | HTTP status | Description | Recovery |
|---|---|---|---|---|
| 104000 | StreamedMessageFinalizeFailed | 500 | The encoder failed to recover after a publish error. One or more updateMessage calls could not recover a failed append pipeline. | Non-fatal. The message may be incomplete on the channel, so check connectivity and decide whether to republish. The run itself is unaffected. |
| 104001 | SessionSubscriptionFailed | 500 | The session could not subscribe and attach to its channel. | Inspect the underlying error. Check the channel capability on the token and the connection state. |
| 104002 | RunCancelHandlerFailed | 500 | The run's onCancel hook threw while the SDK was asking it to authorise a cancel. The run is not cancelled: the SDK never aborts the run. | Check the hook for unhandled exceptions. Delivered to the run's onError when one is set, and on the session's on('error') when it is not. |
| 104003 | RunLifecycleEventPublishFailed | 500 | A run or step lifecycle event failed to publish: ai-run-start, ai-run-suspend, ai-run-end, ai-step-start, or ai-step-end. A failed message publish is 104005 and a failed mid-stream publish is 104008. | Check channel permissions and connection state. Surface the error to the calling HTTP handler so the client's pending send fails. |
| 104004 | SessionClosed | 400 | An operation was attempted on a session, view, or encoder that has already been closed. | Create a new session. Do not reuse a closed session. |
| 104005 | SessionSendFailed | 500, or the non-2xx status the agent-invocation POST returned | A send failed: either the core's channel publish failed, or the Vercel chat transport's agent-invocation POST failed (network error or non-2xx response). | Check the API endpoint URL, network connectivity, and authentication. Inspect the underlying error for details. |
| 104006 | SessionContinuityNotGuaranteed | 500 | The Ably channel lost message continuity. The channel entered FAILED, SUSPENDED, or DETACHED, or re-attached with resumed: false. Active streams can no longer be guaranteed to receive all events. | Surfaced via the session's on('error') on both the client and the agent. End active runs; subsequent operations can resume once the channel reattaches. |
| 104007 | SessionChannelNotReady | 400 | An operation was attempted but the channel is not in a usable state (not ATTACHED or ATTACHING). | Wait for the channel to attach before retrying, or inspect channel state and connection state for the underlying cause. |
| 104008 | RunResponseStreamFailed | 500 | An error occurred while piping a response stream to the session. Either the source event stream threw (LLM provider rate limit, model error, network failure) or an underlying publish failed mid-stream. | Inspect cause for the provider error. End the run with reason 'error'. The error is also returned on StreamResult.error from Run.pipe. |
| 104009 | SessionMessageProcessingFailed | 500 | A session-level subscription callback threw while processing an inbound message. The subscription survives and the session keeps sending and receiving; only that message's processing failed. | Check the callback for unhandled exceptions. Surfaced on the session's on('error'). |
| 104010 | AdoptedRunStartNotObserved | 504 | A fresh process adopting an open run with load() waited for that run's ai-run-start across the live subscription and a bounded history scan, and the timeoutMs bound lapsed without seeing it. Any history-fetch failure is preserved as cause. | Retryable. It usually means a workflow ordering problem where the open activity's run-start has not propagated yet. Retry with a longer timeoutMs. |
| 104011 | SessionHistoryFetchFailed | 500 | Channel history pagination failed after bounded retry. Either the initial channel.history() call or a later page.next() exhausted its retry budget. Also used when a history load fails with an error that is not already an ErrorInfo. | Inspect cause. Retry the load, and check channel permissions if it persists. Surfaced on useView's loadError. |
| 104012 | RunSteerHandlerFailed | 500 | The run's onSteer hook threw. The run is unaffected, because the steering message has already been merged in and only the notification failed. | Check the hook for unhandled exceptions. hasInput() stays authoritative, so the agent's loop still sees the steering message. |
| 104013 | RunCancelRoutingFailed | 500 | The SDK failed while routing an inbound cancel to its target run, so the cancel was neither honoured nor rejected and the run keeps running. | Inspect cause for the underlying throw. The client can publish the cancel again to stop the run. |
Auth and channel errors
Auth and channel errors come from the Ably platform rather than AI Transport. The most common codes you encounter:
| Code | Description | Recovery |
|---|---|---|
| 40142 | Token expired. | Refresh the token (handled automatically with authUrl/authCallback). |
| 40300 | Forbidden. | The token is valid but not authorised for this resource. |
| 80000 | Channel attach failed. | Check the channel name and capability. |
| 90000 | Internal channel error. | Retry the operation. If the error persists, contact support. |
| 93002 | Can only update/delete/append messages on channels with mutableMessages enabled. No channel rule with mutableMessages matches the channel, so AI Transport cannot append stream tokens. This is the most common AI Transport setup failure. | Enable the Message annotations, updates, deletes, and appends rule on the channel. |
The Ably error codes page lists every code the platform returns.
Match an ErrorInfo against a code
errorInfoIs(errorInfo: Ably.ErrorInfo, error: ErrorCode): booleanerrorInfoIs returns true if the ErrorInfo's code matches the supplied ErrorCode. Use it in error handlers to branch on a specific failure by its numeric code.
1
2
3
4
5
6
7
import { errorInfoIs, ErrorCode } from '@ably/ai-transport';
session.on('error', (error) => {
if (errorInfoIs(error, ErrorCode.SessionClosed)) {
reconnect();
}
});Example
Handle session-level and run-level errors separately. Both the client and the agent subscribe to session errors with session.on('error'). The agent additionally uses the run runtime's onError for per-run failures.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { errorInfoIs, ErrorCode } from '@ably/ai-transport';
// Client side: subscribe to session-level errors.
session.on('error', (error) => {
console.error(`Error ${error.code}: ${error.message}`);
if (errorInfoIs(error, ErrorCode.SessionContinuityNotGuaranteed)) {
showReconnectBanner();
}
});
// Agent side: handle session and run-level errors separately.
const agentSession = createAgentSession({ client: ably, channelName, codec });
await agentSession.connect();
agentSession.on('error', (error) => {
console.error('Session error:', error);
});
const run = agentSession.createRun(invocation, {}, {
signal: req.signal,
onError(error) {
console.error('Run error:', error);
},
});
// End a run with reason 'error' when piping fails.
try {
const result = await run.pipe(llmStream);
await run.end({ reason: result.reason });
} catch (error) {
await run.end({ reason: 'error' });
}To give clients specific detail, optionally pass an Ably.ErrorInfo: run.end({ reason: 'error', error: new Ably.ErrorInfo(message, code, statusCode) }). Omit error to end in error without detail and the client sees a generic fallback; either way clients read the error from RunInfo.error and the session error event.