# State recovery

When the connection drops, the user can miss new messages, reactions, and other channel changes. State recovery is how the SDK catches up after it reconnects. It reloads the channels the user had open and updates local state so your UI can show current data.

You choose how recovery behaves with `IStreamClientConfig.StateRecoveryStrategy`. The default is `ReplayEvents`: missed changes are raised through the normal event handlers (`MessageReceived`, `ReactionAdded`, and so on), then channels are reloaded from the server and watched again.

## Choose a strategy

Set the strategy on `StreamClientConfig` before you create the client.

```csharp label="Unity"
using StreamChat.Core;
using StreamChat.Core.Configs;

var config = new StreamClientConfig
{
    StateRecoveryStrategy = StateRecoveryStrategy.ReplayEvents,
};
var client = StreamChatClient.CreateDefaultClient(config);
```

| Value                    | What happens                                                                                                                                    | When to use it                                        |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `ReplayEvents` (default) | The SDK sends missed changes through normal event handlers, then reloads and watches the channels again. `StateRecovered` is raised at the end. | Your UI already updates from events.                  |
| `BatchStateUpdate`       | The SDK updates local channel state without normal event handlers, then raises `StateRecovered`.                                                | Many missed events could make event replay too slow.  |
| `Disabled`               | The SDK does not recover channels. `StateRecovered` is not raised.                                                                              | Your app reloads the channels itself after reconnect. |

Use `ReplayEvents` if your UI updates from event handlers such as `MessageReceived` or `ReactionAdded`. After a long disconnect on a busy livestream, replaying many events at once can make the UI pause for a moment.

Use `BatchStateUpdate` if that pause is a problem. Listen to `StateRecovered` and refresh the UI from channel state.

Use `Disabled` only if you restore state yourself.

## Recovery pipeline

For `ReplayEvents` and `BatchStateUpdate`, recovery works like this:

1. When the connection drops, the SDK saves the ids of channels that were being watched. `WatchedChannels` can be empty during recovery.
2. The first `ConnectUserAsync` after login is a normal connect, not recovery. `StateRecovered` does not fire.
3. On reconnect, the SDK applies changes that happened while you were disconnected, then reloads each channel from the server and starts watching it again.
4. `StateRecovered` is raised once with the recovered channels and any channel ids that could not be recovered.

## StateRecovered

Subscribe on the client. You need this for `BatchStateUpdate` because event handlers such as `MessageReceived` do not fire during recovery. It is also useful for `ReplayEvents`: reloading a channel can update `channel.Messages` without firing `MessageReceived` for every message.

```csharp label="Unity"
client.StateRecovered += args =>
{
    foreach (var channel in args.Channels)
    {
        // Rebuild this channel's UI from channel.Messages and similar collections.
        Debug.Log($"Recovered {channel.Cid}, messages: {channel.Messages.Count}");
    }

    foreach (var cid in args.UnrecoveredChannelCids)
    {
        // This channel could not be recovered. Close or mark its UI; local data for it is out of date.
        Debug.Log($"Could not recover {cid}");
    }
};
```

- `Channels`: channels that were reloaded and watched again. Collections such as `Messages` are up to date when the event is raised.
- `UnrecoveredChannelCids`: channel ids (type and id, for example `messaging:lobby`) that could not be recovered. This can happen if a channel was deleted, the user lost access, or loading failed. Use this to close or mark the related UI.
- `IsComplete`: `true` when every channel the SDK tried to recover succeeded. Channels beyond the 100-channel recovery limit are not listed in `UnrecoveredChannelCids`.

Recovery adds the newest messages to what was already loaded. A long disconnect can leave a gap in `Messages`: older loaded messages, then a jump to the newest messages. `LoadOlderMessagesAsync` cannot fill that gap, because it loads messages older than the oldest message already in the list.

## Batch state update

`BatchStateUpdate` uses the same recovery flow as `ReplayEvents`, but missed events update local state without raising normal event handlers. Refresh the UI from `StateRecovered`.

Some events are still raised because they are not stored on a channel:

- `CustomEventReceived`
- `ChannelDeleted`
- membership and invite notifications (`AddedToChannelAsMember`, `ChannelInviteReceived`, and similar)

Do not expect `MessageReceived` or `ReactionAdded` during `BatchStateUpdate` recovery.

```csharp label="Unity"
var config = new StreamClientConfig
{
    StateRecoveryStrategy = StateRecoveryStrategy.BatchStateUpdate,
};
var client = StreamChatClient.CreateDefaultClient(config);

client.StateRecovered += args =>
{
    foreach (var channel in args.Channels)
    {
        Debug.Log($"Recovered {channel.Cid}, messages: {channel.Messages.Count}");
    }

    foreach (var cid in args.UnrecoveredChannelCids)
    {
        Debug.Log($"Could not recover {cid}");
    }
};
```

## Limits

If the user watches many channels or stays disconnected for a long time:

- The SDK recovers at most 100 channels (the ones with the most recent activity). Extra channels keep old local state and are not watched again. A warning is logged.
- If the SDK has no timestamp for the last event, or the disconnect lasted more than 30 days, missed events are not fetched. The SDK still reloads and watches the channels.
- If too many events accumulated, the server rejects the event fetch. The SDK still reloads channel state from a query.
- If recovery fails for one channel, the others still recover.
- If the connection drops again during recovery, that recovery is discarded and a new one starts.

## Disabled

The SDK does not restore state, does not start watching again, and does not raise `StateRecovered`. Local state and `WatchedChannels` stay as they were before the disconnect.

When `ConnectionState` is `Connected` again, query the channels your app needs. That call refreshes state and starts watching.

<Admonition type="warning">

Do not call `WatchAsync` to recover. `IStreamChannel.IsWatched` can still be `true` from before the disconnect, so `WatchAsync` does nothing.

</Admonition>

```csharp label="Unity"
using System.Linq;
using StreamChat.Core;
using StreamChat.Core.Configs;
using StreamChat.Core.QueryBuilders.Filters.Channels;

var config = new StreamClientConfig
{
    StateRecoveryStrategy = StateRecoveryStrategy.Disabled,
};
var client = StreamChatClient.CreateDefaultClient(config);

client.Connected += async _ =>
{
    // With Disabled, WatchedChannels is not cleared on disconnect, so you can read it here.
    var cids = client.WatchedChannels.Select(channel => channel.Cid).ToArray();
    if (cids.Length == 0)
    {
        return;
    }

    // Do not call WatchAsync: IsWatched is still true, so it does nothing.
    await client.QueryChannelsAsync(new[] { ChannelFilter.Cid.In(cids) }, limit: 30);
};
```

If you save channel ids yourself, do it before the disconnect. With `Disabled`, you can also read `WatchedChannels` after `Connected` because the list is not cleared.

See [Querying Channels](https://getstream.io/chat/docs/unity/query-channels/) for filters, sort, and pagination.

## Related

- [Client configuration](https://getstream.io/chat/docs/unity/client-configuration/) for every `IStreamClientConfig` property
- [Connection lifecycle](https://getstream.io/chat/docs/unity/connection-lifecycle/) for pause, resume, and logout
- [Events](https://getstream.io/chat/docs/unity/event-object/) for `StateRecovered` and connection callbacks


---

This page was last updated at 2026-09-02T15:00:32.093Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/unity/state-recovery/](https://getstream.io/chat/docs/unity/state-recovery/).