chat
Version:
Unified chat abstraction for Slack, Teams, Google Chat, and Discord
160 lines (131 loc) • 5.25 kB
text/mdx
---
title: Error Handling
description: Handle rate limits, unsupported features, and other errors from adapters.
type: guide
prerequisites:
- /docs/usage
---
The SDK provides typed error classes for common failure scenarios. All errors are importable from the `chat` package.
```typescript
import { ChatError, RateLimitError, NotImplementedError, LockError } from "chat";
```
## Error types
### ChatError
Base error class for all SDK errors. Every error below extends `ChatError`. The `code` property carries a machine-readable identifier you can branch on:
| Code | Thrown by | Meaning |
|------|-----------|---------|
| `NOT_SUPPORTED` | `bot.openDM`, `bot.getUser` | The resolved adapter doesn't implement this method |
| `INVALID_THREAD_ID` | `bot.thread`, internal routing | Thread ID does not match the `adapter:channel:thread` shape |
| `INVALID_CHANNEL_ID` | `bot.channel` | Channel ID does not match the `adapter:channel` shape |
| `ADAPTER_NOT_FOUND` | `bot.thread`, `bot.channel` | Thread/channel ID references an adapter that wasn't registered on this `Chat` instance |
| `AMBIGUOUS_USER_ID` | `bot.getUser`, `bot.openDM` | Numeric user ID could match more than one registered adapter (Discord/Telegram/GitHub) |
| `UNKNOWN_USER_ID_FORMAT` | `bot.getUser`, `bot.openDM` | The `userId` doesn't match any platform's known ID format |
| `RATE_LIMITED` | Any platform call | Platform returned 429; see `RateLimitError` below |
| `NOT_IMPLEMENTED` | Any platform call | The adapter doesn't implement this feature; see `NotImplementedError` below |
| `LOCK_FAILED` | Inbound message routing | Distributed lock was busy; see `LockError` below |
<TypeTable
type={{
message: {
description: 'Human-readable error message.',
type: 'string',
},
code: {
description: 'Machine-readable error code (see table above).',
type: 'string',
},
cause: {
description: 'Original error that caused this one.',
type: 'unknown | undefined',
},
}}
/>
### RateLimitError
Thrown when a platform API returns a 429 response. The `retryAfterMs` property tells you how long to wait before retrying.
```typescript title="lib/bot.ts" lineNumbers
import { RateLimitError } from "chat";
try {
await thread.post("Hello!");
} catch (error) {
if (error instanceof RateLimitError) {
console.log(`Rate limited, retry after ${error.retryAfterMs}ms`);
}
}
```
<TypeTable
type={{
code: {
description: 'Always "RATE_LIMITED".',
type: 'string',
},
retryAfterMs: {
description: 'Milliseconds to wait before retrying.',
type: 'number | undefined',
},
}}
/>
### NotImplementedError
Thrown when you call a feature that a platform doesn't support. For example, calling `addReaction()` on Teams or `schedule()` on adapters without native scheduling support.
```typescript title="lib/bot.ts" lineNumbers
import { NotImplementedError } from "chat";
try {
await thread.addReaction(emoji.thumbs_up);
} catch (error) {
if (error instanceof NotImplementedError) {
console.log(`Feature not supported: ${error.feature}`);
}
}
```
<TypeTable
type={{
code: {
description: 'Always "NOT_IMPLEMENTED".',
type: 'string',
},
feature: {
description: 'Name of the unsupported feature.',
type: 'string | undefined',
},
}}
/>
See the [feature matrix](/docs/adapters) for which features are supported on each platform.
### LockError
Thrown when the SDK fails to acquire a distributed lock on a thread (used to prevent concurrent processing of messages in the same thread). You can control this behavior with the [`onLockConflict`](/docs/usage#configuration-options) option — set it to `'force'` to release the existing lock instead of throwing.
<TypeTable
type={{
code: {
description: 'Always "LOCK_FAILED".',
type: 'string',
},
}}
/>
## Adapter errors
Adapters also throw specialized errors from the `-adapter/shared` package:
| Error | Code | Description |
|-------|------|-------------|
| `AdapterRateLimitError` | `RATE_LIMITED` | Platform rate limit hit, includes `retryAfter` in seconds |
| `AuthenticationError` | `AUTH_FAILED` | Invalid or expired credentials |
| `ResourceNotFoundError` | `NOT_FOUND` | Requested resource (channel, message) doesn't exist |
| `PermissionError` | `PERMISSION_DENIED` | Bot lacks required permissions/scopes |
| `ValidationError` | `VALIDATION_ERROR` | Invalid input data (e.g. message too long) |
| `NetworkError` | `NETWORK_ERROR` | Connectivity issue with platform API |
## Catching errors
Use `instanceof` to handle specific error types:
```typescript title="lib/bot.ts" lineNumbers
import { RateLimitError, NotImplementedError } from "chat";
bot.onNewMention(async (thread, message) => {
try {
await thread.post("Processing...");
await thread.addReaction(emoji.eyes);
} catch (error) {
if (error instanceof RateLimitError) {
// Wait and retry
await new Promise((r) => setTimeout(r, error.retryAfterMs ?? 5000));
await thread.post("Processing...");
} else if (error instanceof NotImplementedError) {
// Skip unsupported features gracefully
} else {
throw error;
}
}
});
```