The ACP SDK uses JSON-RPC 2.0 error handling with the RequestError class. All protocol-level errors are represented using standardized error codes.
JSON-RPC error class for representing errors during method execution.
class RequestError extends Error {
constructor(code: number, message: string, data?: unknown);
code: number;
data?: unknown;
// Standard error factories
static parseError(data?: unknown, additionalMessage?: string): RequestError;
static invalidRequest(data?: unknown, additionalMessage?: string): RequestError;
static methodNotFound(method: string): RequestError;
static invalidParams(data?: unknown, additionalMessage?: string): RequestError;
static internalError(data?: unknown, additionalMessage?: string): RequestError;
// ACP-specific errors
static authRequired(data?: unknown, additionalMessage?: string): RequestError;
static resourceNotFound(uri?: string): RequestError;
// Conversion methods
toResult<T>(): Result<T>;
toErrorResponse(): ErrorResponse;
}Creates a new RequestError with a specific code, message, and optional data.
constructor(code: number, message: string, data?: unknown)Parameters:
code (number): JSON-RPC error codemessage (string): Human-readable error messagedata (unknown, optional): Additional error dataUsage Example:
throw new RequestError(-32001, 'Custom error', { detail: 'explanation' });Invalid JSON was received. An error occurred while parsing the JSON text.
static parseError(data?: unknown, additionalMessage?: string): RequestErrorError Code: -32700
Usage Example:
try {
JSON.parse(invalidJson);
} catch {
throw RequestError.parseError({ received: invalidJson });
}The JSON sent is not a valid Request object.
static invalidRequest(data?: unknown, additionalMessage?: string): RequestErrorError Code: -32600
Usage Example:
if (!isValidRequest(message)) {
throw RequestError.invalidRequest(message, 'Missing required field: method');
}The method does not exist or is not available.
static methodNotFound(method: string): RequestErrorError Code: -32601
Usage Example:
if (!supportedMethods.includes(method)) {
throw RequestError.methodNotFound(method);
}Invalid method parameter(s).
static invalidParams(data?: unknown, additionalMessage?: string): RequestErrorError Code: -32602
Usage Example:
if (params.sessionId == null) {
throw RequestError.invalidParams({ missing: 'sessionId' });
}Internal JSON-RPC error.
static internalError(data?: unknown, additionalMessage?: string): RequestErrorError Code: -32603
Usage Example:
try {
await processRequest(params);
} catch (error) {
throw RequestError.internalError({ originalError: error.message });
}Authentication is required before proceeding.
static authRequired(data?: unknown, additionalMessage?: string): RequestErrorError Code: -32000
Usage Example:
if (!isAuthenticated(clientId)) {
throw RequestError.authRequired({
availableMethods: ['oauth', 'api_key']
});
}A resource (such as a file) was not found.
static resourceNotFound(uri?: string): RequestErrorError Code: -32002
Usage Example:
try {
await fs.readFile(filePath);
} catch {
throw RequestError.resourceNotFound('file:///path/to/file.txt');
}Converts the error to a Result object for JSON-RPC responses.
toResult<T>(): Result<T>Returns:
{
error: {
code: number;
message: string;
data?: unknown;
}
}Usage Example:
try {
const result = await processRequest(params);
return { result };
} catch (error) {
if (error instanceof RequestError) {
return error.toResult();
}
throw error;
}Converts the error to an ErrorResponse object.
toErrorResponse(): ErrorResponseReturns:
{
code: number;
message: string;
data?: unknown;
}import { Agent, RequestError } from '@agentclientprotocol/sdk';
const agent: Agent = {
async newSession(params) {
// Check authentication
if (!isAuthenticated()) {
throw RequestError.authRequired({
message: 'Please authenticate before creating a session'
});
}
// Validate parameters
if (!params.mcpServers) {
throw RequestError.invalidParams(
{ missing: 'mcpServers' },
'mcpServers parameter is required'
);
}
try {
const sessionId = await createSession(params);
return {
sessionId,
availableModes: getAvailableModes(),
currentMode: 'code'
};
} catch (error) {
// Convert internal errors
throw RequestError.internalError({
detail: error.message
});
}
}
};import { Client, RequestError } from '@agentclientprotocol/sdk';
const client: Client = {
async readTextFile(params) {
try {
const content = await fs.readFile(params.uri, 'utf-8');
return { content };
} catch (error) {
if (error.code === 'ENOENT') {
throw RequestError.resourceNotFound(params.uri);
}
throw RequestError.internalError({
fsError: error.message
});
}
},
async createTerminal(params) {
// Validate capability
if (!clientCapabilities.terminal) {
throw RequestError.methodNotFound('terminal/create');
}
try {
const terminal = spawn(params.command, params.args);
const terminalId = registerTerminal(terminal);
return { terminalId };
} catch (error) {
throw RequestError.internalError({
spawnError: error.message
});
}
}
};import { ClientSideConnection, RequestError } from '@agentclientprotocol/sdk';
const connection = new ClientSideConnection((agent) => client, stream);
try {
const session = await connection.newSession({ mcpServers: [] });
console.log('Session created:', session.sessionId);
} catch (error) {
if (error instanceof RequestError) {
switch (error.code) {
case -32000: // authRequired
console.error('Authentication required');
await connection.authenticate({
method: 'oauth',
credentials: await getCredentials()
});
// Retry newSession
break;
case -32601: // methodNotFound
console.error('Agent does not support this method');
break;
case -32602: // invalidParams
console.error('Invalid parameters:', error.data);
break;
case -32603: // internalError
console.error('Agent internal error:', error.message);
if (error.data) {
console.error('Details:', error.data);
}
break;
default:
console.error(`Error ${error.code}: ${error.message}`);
}
} else {
// Non-protocol error
throw error;
}
}The SDK automatically converts Zod validation errors to invalidParams errors:
// In agent/client implementation
async initialize(params) {
// SDK validates params using Zod schema
// If validation fails, automatically throws:
// RequestError.invalidParams(zodError.format())
// Your code only runs with valid params
return { ... };
}Errors are transmitted as JSON-RPC 2.0 error responses:
{
"jsonrpc": "2.0",
"id": 123,
"error": {
"code": -32002,
"message": "Resource not found: file:///path/to/file.txt",
"data": {
"uri": "file:///path/to/file.txt"
}
}
}| Code | Name | Description |
|---|---|---|
| -32700 | Parse Error | Invalid JSON was received |
| -32600 | Invalid Request | The JSON is not a valid Request object |
| -32601 | Method Not Found | The method does not exist |
| -32602 | Invalid Params | Invalid method parameter(s) |
| -32603 | Internal Error | Internal JSON-RPC error |
| -32000 | Auth Required | Authentication required (ACP-specific) |
| -32002 | Resource Not Found | Resource not found (ACP-specific) |
Use Factory Methods: Always use static factory methods instead of constructing errors manually
// Good
throw RequestError.resourceNotFound(uri);
// Avoid
throw new RequestError(-32002, 'Resource not found');Include Useful Data: Provide additional context in the data parameter
throw RequestError.invalidParams({
missing: 'sessionId',
received: params
});Catch and Convert: Convert application errors to RequestError
try {
await operation();
} catch (error) {
throw RequestError.internalError({
originalError: error.message
});
}Check Error Codes: Always check error codes when catching
if (error instanceof RequestError && error.code === -32000) {
// Handle auth required
}Don't Leak Secrets: Be careful not to include sensitive data in error details
// Bad - leaks password
throw RequestError.authRequired({ password: params.password });
// Good
throw RequestError.authRequired({ method: params.method });Error class