chat
Version:
Unified chat abstraction for Slack, Teams, Google Chat, and Discord
45 lines (31 loc) • 1.94 kB
text/mdx
title: State Adapters
description: Pluggable state adapters for thread subscriptions, distributed locking, and caching.
type: overview
prerequisites:
- /docs/getting-started
State adapters handle persistent storage for thread subscriptions, distributed locks (to prevent duplicate processing), and caching. You must provide a state adapter when creating a `Chat` instance. Browse all available state adapters on the [Adapters](/adapters) page.
## What state adapters manage
### Thread subscriptions
When your bot calls `thread.subscribe()`, the state adapter persists that subscription. On subsequent webhooks, the SDK checks subscriptions to route messages to `onSubscribedMessage` handlers. With a production adapter, subscriptions survive restarts and work across multiple instances.
### Distributed locking
When a webhook arrives, the SDK acquires a lock on the thread to prevent duplicate processing. This is critical for serverless deployments where multiple instances may receive the same event.
By default, if a lock is already held, the incoming message is dropped with a `LockError`. For long-running handlers (e.g. AI agent streaming), you can configure `onLockConflict: 'force'` to force-release the existing lock and allow the new message through:
```typescript
const chat = new Chat({
userName: 'my-bot',
adapters: { slack },
state: createRedisState(),
onLockConflict: 'force',
});
```
You can also pass a callback for custom logic:
```typescript
onLockConflict: (threadId, message) => {
return message.text.includes('stop') ? 'force' : 'drop';
}
```
Note that force-releasing a lock does not cancel the previous handler — it continues running. Only the lock is released, so two handlers may briefly run concurrently on the same thread.
### Caching
State adapters provide key-value storage with TTL for thread state (`thread.setState()`), message deduplication, and other internal caching.