Skip to content

Errors

All errors extend the base AgentExpressError class. Every error has a machine-readable code, a retryable flag, and an optional cause for error chaining.

import {
AgentExpressError,
AbortError,
ModelError,
RateLimitError,
ContextOverflowError,
ContentFilterError,
AuthenticationError,
NetworkError,
ToolDeniedError,
ToolExecutionError,
SessionClosedError,
SessionBusyError,
StructuredOutputParseError,
StructuredOutputValidationError,
BudgetExceededError,
InputGuardrailError,
OutputGuardrailError,
TurnTimeoutError,
} from "agent-express"

AgentExpressError
├── AbortError
├── ModelError
│ ├── RateLimitError
│ ├── ContextOverflowError
│ ├── ContentFilterError
│ ├── AuthenticationError
│ └── NetworkError
├── ToolDeniedError
├── ToolExecutionError
├── SessionClosedError
├── SessionBusyError
├── StructuredOutputParseError
├── StructuredOutputValidationError
├── BudgetExceededError
├── InputGuardrailError
├── OutputGuardrailError
└── TurnTimeoutError

Base class for all framework errors. Extends Error.

PropertyTypeDescription
codestringMachine-readable error code (e.g., "ABORT", "RATE_LIMIT", "TOOL_DENIED").
retryablebooleanWhether retry middleware should attempt to retry this error.
causeError?Original error that caused this one, if any.
try {
await agent.run("test").result
} catch (err) {
if (err instanceof AgentExpressError) {
console.log(err.code, err.retryable)
}
}

Thrown when ctx.abort(reason) is called in any middleware hook. This is a hard stop — it unwinds the entire onion stack and rejects the AgentRun.result promise.

PropertyTypeDescription
code"ABORT"
retryablefalse
reasonstringThe reason passed to ctx.abort().
// In a middleware:
turn: async (ctx, next) => {
if (ctx.state.totalCost > 1.00) ctx.abort("Budget exceeded")
await next()
}

Base class for errors originating from LLM model providers. Subtypes cover specific failure modes.

PropertyTypeDescription
code"MODEL_ERROR"
providerstringProvider name (e.g., "anthropic", "openai").
statusCodenumber?HTTP status code from the provider API, if available.

Extends: ModelError

HTTP 429 — the provider rate-limited the request. Retryable with backoff.

PropertyTypeDescription
code"RATE_LIMIT"
retryabletrue
statusCode429
retryAfternumber?Suggested wait time in seconds before retrying, if the provider sent one.
if (err instanceof RateLimitError) {
await sleep((err.retryAfter ?? 5) * 1000)
}

Extends: ModelError

The prompt exceeded the model’s context window. Retryable after truncation (e.g., by memory.compaction()).

PropertyTypeDescription
code"CONTEXT_OVERFLOW"
retryabletrue
statusCode400

Extends: ModelError

The provider blocked the request due to content policy. Not retryable.

PropertyTypeDescription
code"CONTENT_FILTER"
retryablefalse
statusCode400

Extends: ModelError

Invalid or missing API key. Not retryable.

PropertyTypeDescription
code"AUTH_ERROR"
retryablefalse
statusCode401

Extends: ModelError

Network-level failure (DNS, TCP, TLS). Retryable.

PropertyTypeDescription
code"NETWORK_ERROR"
retryabletrue
statusCodeundefined

Thrown when ctx.deny(reason) is called in a tool hook. This is a soft failure — the tool is not executed, and the LLM receives an error message so it can try a different approach. Does not unwind the stack.

PropertyTypeDescription
code"TOOL_DENIED"
retryablefalse
toolNamestringName of the tool that was denied.

A tool’s execute() function threw an error. Wrapped with the tool name for context.

PropertyTypeDescription
code"TOOL_EXECUTION"
retryablefalse
toolNamestringName of the tool that failed.
causeErrorThe original error thrown by the tool.

Thrown when session.run() is called on a session that has been closed.

PropertyTypeDescription
code"SESSION_CLOSED"
retryablefalse
sessionIdstringID of the closed session.

Thrown when session.run() is called while a turn is already in progress. Sessions are single-threaded — wait for the current turn to complete before starting another.

PropertyTypeDescription
code"SESSION_BUSY"
retryablefalse
sessionIdstringID of the busy session.

Thrown by guard.budget() when the session cost limit is exceeded (only when onLimit: "error", the default).

PropertyTypeDescription
code"BUDGET_EXCEEDED"
retryablefalse
costnumberAccumulated cost in USD at the time of the error.
limitnumberConfigured limit in USD.

Thrown by guard.input() when the input validator returns { ok: false }.

PropertyTypeDescription
code"INPUT_VALIDATION"
retryablefalse
reasonstringReason the input was rejected.

Thrown by guard.output() when the output validator blocks a response and onBlock: "error" is set.

PropertyTypeDescription
code"OUTPUT_BLOCKED"
retryablefalse
reasonstringReason the response was blocked.

Thrown by guard.timeout() when a turn or model call exceeds its time limit.

PropertyTypeDescription
code"TIMEOUT"
retryablefalse
timeoutMsnumberTimeout that was exceeded, in milliseconds.
scope"turn" | "model"Whether this was a turn timeout or model call timeout.

Thrown when the model returns text that is not valid JSON for structured output.

PropertyTypeDescription
code"STRUCTURED_OUTPUT_PARSE"
retryablefalse
rawTextstringThe raw model response (first 500 characters).

Thrown when the parsed JSON does not match the expected Zod schema.

PropertyTypeDescription
code"STRUCTURED_OUTPUT_VALIDATION"
retryablefalse
issuesunknown[]Zod validation issues array.

Use instanceof checks to handle specific error types:

import {
AgentExpressError,
BudgetExceededError,
RateLimitError,
InputGuardrailError,
} from "agent-express"
try {
const result = await agent.run("Hello").result
} catch (err) {
if (err instanceof BudgetExceededError) {
console.log(`Over budget: $${err.cost} / $${err.limit}`)
} else if (err instanceof RateLimitError) {
console.log(`Rate limited, retry after ${err.retryAfter}s`)
} else if (err instanceof InputGuardrailError) {
console.log(`Input blocked: ${err.reason}`)
} else if (err instanceof AgentExpressError) {
console.log(`Agent error [${err.code}]: ${err.message}`)
}
}