> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentclientprotocol/typescript-sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Handling errors in ACP connections using RequestError and JSON-RPC error codes

The ACP TypeScript SDK follows JSON-RPC 2.0 error conventions, providing structured error handling for all requests and responses.

## RequestError Class

The `RequestError` class represents JSON-RPC errors that can be thrown from request handlers:

```typescript theme={null}
export class RequestError extends Error {
  constructor(
    public code: number,
    message: string,
    public data?: unknown,
  )
}
```

### Properties

* `code`: Numeric error code (see [Error Codes](#error-codes) below)
* `message`: Human-readable error description
* `data`: Optional additional error information

<Note>
  `RequestError` extends the standard JavaScript `Error` class, so it can be caught and thrown like any other error.
</Note>

## Error Codes

The SDK provides static factory methods for standard JSON-RPC error codes:

### Parse Error (-32700)

Invalid JSON was received:

```typescript theme={null}
throw RequestError.parseError(
  { received: rawInput },
  'Malformed JSON in request'
);
```

**Signature:**

```typescript theme={null}
static parseError(
  data?: unknown,
  additionalMessage?: string
): RequestError
```

### Invalid Request (-32600)

The JSON is valid but not a proper request object:

```typescript theme={null}
throw RequestError.invalidRequest(
  { missing: 'method' },
  'Request missing required method field'
);
```

**Signature:**

```typescript theme={null}
static invalidRequest(
  data?: unknown,
  additionalMessage?: string
): RequestError
```

### Method Not Found (-32601)

The requested method doesn't exist or isn't available:

```typescript theme={null}
throw RequestError.methodNotFound('session/invalid');
```

**Signature:**

```typescript theme={null}
static methodNotFound(method: string): RequestError
```

<Warning>
  Always throw `methodNotFound` for unknown methods in your `extMethod` handlers to maintain protocol compliance.
</Warning>

### Invalid Params (-32602)

The method parameters are invalid:

```typescript theme={null}
throw RequestError.invalidParams(
  { field: 'sessionId', issue: 'required' },
  'Missing required sessionId parameter'
);
```

**Signature:**

```typescript theme={null}
static invalidParams(
  data?: unknown,
  additionalMessage?: string
): RequestError
```

### Internal Error (-32603)

An internal server error occurred:

```typescript theme={null}
try {
  await processRequest();
} catch (err) {
  throw RequestError.internalError(
    { details: err.message },
    'Failed to process request'
  );
}
```

**Signature:**

```typescript theme={null}
static internalError(
  data?: unknown,
  additionalMessage?: string
): RequestError
```

### Custom ACP Errors

The SDK also includes ACP-specific error codes:

#### Authentication Required (-32000)

```typescript theme={null}
if (!this.isAuthenticated) {
  throw RequestError.authRequired(
    { methods: ['oauth', 'api_key'] },
    'Please authenticate before creating a session'
  );
}
```

**Signature:**

```typescript theme={null}
static authRequired(
  data?: unknown,
  additionalMessage?: string
): RequestError
```

#### Resource Not Found (-32002)

```typescript theme={null}
const file = await this.findFile(uri);
if (!file) {
  throw RequestError.resourceNotFound(uri);
}
```

**Signature:**

```typescript theme={null}
static resourceNotFound(uri?: string): RequestError
```

## Throwing Errors

Throw `RequestError` from any request handler to send a structured error response:

```typescript theme={null}
import * as acp from '@agentclientprotocol/acp';

class MyAgent implements acp.Agent {
  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    // Check if session exists
    const session = this.sessions.get(params.sessionId);
    if (!session) {
      throw RequestError.invalidParams(
        { sessionId: params.sessionId },
        'Session not found'
      );
    }
    
    // Check if user has permission
    if (!session.isAuthorized) {
      throw RequestError.authRequired(
        { sessionId: params.sessionId },
        'Session requires authentication'
      );
    }
    
    // Process the prompt...
    return { stopReason: 'end_turn' };
  }
  
  async extMethod(
    method: string,
    params: Record<string, unknown>,
  ): Promise<Record<string, unknown>> {
    switch (method) {
      case 'example.com/my_method':
        return { result: 'ok' };
      
      default:
        // Always throw methodNotFound for unknown methods
        throw RequestError.methodNotFound(method);
    }
  }
}
```

## Catching Errors

When calling methods on the connection, catch errors to handle failures:

```typescript theme={null}
try {
  const response = await connection.prompt({
    sessionId: 'abc123',
    prompt: [{ type: 'text', text: 'Hello' }],
  });
  
  console.log('Success:', response);
} catch (error) {
  // error is the ErrorResponse object from JSON-RPC
  if (typeof error === 'object' && error !== null && 'code' in error) {
    const jsonRpcError = error as acp.ErrorResponse;
    
    switch (jsonRpcError.code) {
      case -32602: // Invalid params
        console.error('Invalid parameters:', jsonRpcError.data);
        break;
      
      case -32000: // Auth required
        console.error('Authentication needed');
        await authenticate();
        break;
      
      default:
        console.error('Error:', jsonRpcError.message);
    }
  }
}
```

<Info>
  When a request fails, the promise is rejected with the JSON-RPC `ErrorResponse` object, not a `RequestError` instance.
</Info>

## Error Response Type

The error response follows the JSON-RPC 2.0 specification:

```typescript theme={null}
export type ErrorResponse = {
  code: number;
  message: string;
  data?: unknown;
};
```

## Automatic Error Handling

The SDK automatically handles certain errors:

### Zod Validation Errors

If a request handler receives invalid parameters, the SDK catches Zod validation errors and converts them to `invalidParams` errors:

```typescript theme={null}
// This is handled automatically by the SDK
const validatedParams = validate.zPromptRequest.parse(params);
// If validation fails, SDK throws RequestError.invalidParams(error.format())
```

### Unexpected Errors

If an unexpected error is thrown from a request handler, the SDK converts it to an `internalError`:

```typescript theme={null}
class MyAgent implements acp.Agent {
  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    // If this throws an unexpected error:
    const result = await someUnreliableOperation();
    
    // The SDK automatically catches it and returns:
    // RequestError.internalError({ details: error.message })
  }
}
```

## Best Practices

<Warning>
  **Do:**

  * Use the appropriate error code for each error type
  * Include helpful error messages
  * Add structured `data` for debugging
  * Throw `methodNotFound` for unknown extension methods

  **Don't:**

  * Throw generic `Error` objects (use `RequestError` instead)
  * Include sensitive data in error messages
  * Swallow errors without handling them
  * Use error codes outside the JSON-RPC specification
</Warning>

## Custom Error Codes

For application-specific errors, use custom error codes in the range `-32000` to `-32099` (reserved for implementation-defined errors):

```typescript theme={null}
class CustomErrors {
  static rateLimitExceeded(): RequestError {
    return new RequestError(
      -32001,
      'Rate limit exceeded',
      {
        retryAfter: 60,
        limit: 100,
      }
    );
  }
  
  static quotaExceeded(): RequestError {
    return new RequestError(
      -32003,
      'Quota exceeded',
      {
        quotaType: 'tokens',
        limit: 1000000,
        used: 1000000,
      }
    );
  }
}

// Use in your handlers
class MyAgent implements acp.Agent {
  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    if (this.isRateLimited(params.sessionId)) {
      throw CustomErrors.rateLimitExceeded();
    }
    
    // Process prompt...
  }
}
```

## Error Handling Example

Complete example showing comprehensive error handling:

```typescript theme={null}
import * as acp from '@agentclientprotocol/acp';

class RobustAgent implements acp.Agent {
  private sessions = new Map<string, Session>();
  
  async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResponse> {
    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: { loadSession: false },
    };
  }
  
  async newSession(params: acp.NewSessionRequest): Promise<acp.NewSessionResponse> {
    // Validate working directory exists
    if (!await this.directoryExists(params.cwd)) {
      throw RequestError.invalidParams(
        { cwd: params.cwd },
        'Working directory does not exist'
      );
    }
    
    const sessionId = crypto.randomUUID();
    this.sessions.set(sessionId, new Session(params));
    
    return { sessionId };
  }
  
  async authenticate(params: acp.AuthenticateRequest): Promise<void> {
    // Check if authentication method is supported
    const supportedMethods = ['api_key', 'oauth'];
    if (!supportedMethods.includes(params.authMethod.id)) {
      throw RequestError.invalidParams(
        { supportedMethods },
        `Unsupported authentication method: ${params.authMethod.id}`
      );
    }
    
    // Validate credentials
    try {
      await this.validateCredentials(params.authMethod);
    } catch (err) {
      throw RequestError.authRequired(
        { reason: 'invalid_credentials' },
        'Invalid or expired credentials'
      );
    }
  }
  
  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    // Validate session
    const session = this.sessions.get(params.sessionId);
    if (!session) {
      throw RequestError.invalidParams(
        { sessionId: params.sessionId },
        'Session not found or expired'
      );
    }
    
    try {
      // Process the prompt
      await session.processPrompt(params.prompt);
      return { stopReason: 'end_turn' };
    } catch (err) {
      // Convert known errors to RequestError
      if (err instanceof QuotaExceededError) {
        throw RequestError.internalError(
          { quotaType: err.quotaType },
          'Quota exceeded'
        );
      }
      
      // Re-throw RequestErrors as-is
      if (err instanceof RequestError) {
        throw err;
      }
      
      // Wrap unexpected errors
      throw RequestError.internalError(
        { error: err.message },
        'Failed to process prompt'
      );
    }
  }
  
  async cancel(params: acp.CancelNotification): Promise<void> {
    const session = this.sessions.get(params.sessionId);
    if (session) {
      session.cancel();
    }
    // Don't throw errors in notification handlers
  }
}
```

## Related

* [Extension Methods](/advanced/extension-methods) - Throwing errors in custom methods
* [Connection Lifecycle](/advanced/connection-lifecycle) - Handling connection errors
