diff --git a/src/data/nav/chat.ts b/src/data/nav/chat.ts
index 02fa2527f1..329e23a4f1 100644
--- a/src/data/nav/chat.ts
+++ b/src/data/nav/chat.ts
@@ -226,6 +226,10 @@ export default {
name: 'Livestream chat',
link: '/docs/guides/chat/build-livestream',
},
+ {
+ name: 'Handling discontinuity',
+ link: '/docs/guides/chat/handling-discontinuity',
+ },
],
},
],
diff --git a/src/data/nav/pubsub.ts b/src/data/nav/pubsub.ts
index 05aa6c7f2a..06f9022903 100644
--- a/src/data/nav/pubsub.ts
+++ b/src/data/nav/pubsub.ts
@@ -345,6 +345,10 @@ export default {
name: 'Dashboards and visualizations',
link: '/docs/guides/pub-sub/dashboards-and-visualizations',
},
+ {
+ name: 'Handling discontinuity',
+ link: '/docs/guides/pub-sub/handling-discontinuity',
+ },
],
},
],
diff --git a/src/pages/docs/chat/rooms/messages.mdx b/src/pages/docs/chat/rooms/messages.mdx
index d4d8dc2368..78089b4578 100644
--- a/src/pages/docs/chat/rooms/messages.mdx
+++ b/src/pages/docs/chat/rooms/messages.mdx
@@ -168,6 +168,64 @@ Messages are sent to users as soon as they [attach](/docs/chat/rooms#attach) to
The [`detach()`](/docs/chat/rooms#detach) method detaches a user from the room. At that point a user will no longer receive any messages that are sent to the room.
+## Handle discontinuity
+
+If a client experiences a period of disconnection longer than two minutes, some messages may be missed. The Chat SDK provides a Chat-specific `onDiscontinuity()` handler at the room level to detect when message continuity is lost, so that you can recover missed messages using [history](/docs/chat/rooms/history).
+
+Register the handler when setting up your room:
+
+
+```javascript
+const { off } = room.onDiscontinuity((reason) => {
+ console.log('Discontinuity detected:', reason);
+ // Re-fetch messages from the point of re-subscription
+ room.messages.historyBeforeSubscribe({ limit: 50 }).then((history) => {
+ // Refresh your message list with recovered messages
+ });
+});
+
+// To remove the listener
+off();
+```
+
+```react
+import { useMessages } from '@ably/chat/react';
+
+const MyComponent = () => {
+ useMessages({
+ onDiscontinuity: (error) => {
+ console.log('Discontinuity detected:', error);
+ // Trigger recovery, for example re-fetch message history
+ },
+ });
+
+ return
...
;
+};
+```
+
+```swift
+let subscription = room.onDiscontinuity()
+for await error in subscription {
+ print("Discontinuity detected: \(error)")
+ // Fetch missed messages and merge into your message list
+}
+```
+
+```kotlin
+val (off) = room.onDiscontinuity { reason: ErrorInfo ->
+ println("Discontinuity detected: $reason")
+ // Fetch missed messages and merge into your message list
+}
+
+// To remove the listener
+off()
+```
+
+
+
+
## Send a message
diff --git a/src/pages/docs/guides/chat/handling-discontinuity.mdx b/src/pages/docs/guides/chat/handling-discontinuity.mdx
new file mode 100644
index 0000000000..1a7f602b39
--- /dev/null
+++ b/src/pages/docs/guides/chat/handling-discontinuity.mdx
@@ -0,0 +1,109 @@
+---
+title: "Guide: Handle discontinuity in Chat"
+meta_description: "Detect and recover from message discontinuity in Ably Chat applications. Learn to use the onDiscontinuity handler and historyBeforeSubscribe to recover missed messages."
+meta_keywords: "discontinuity, message continuity, onDiscontinuity, Chat, message recovery, missed messages, historyBeforeSubscribe"
+---
+
+When a client experiences a period of disconnection longer than the two-minute recovery window, or when Ably signals a loss of message continuity, your application may have missed messages. This is called a *discontinuity*. This guide explains how to detect and recover from discontinuities in Chat applications.
+
+
+
+## What causes discontinuity
+
+Discontinuity occurs when the Ably SDK cannot guarantee that all messages have been delivered to the client. The most common causes are:
+
+- Network disconnection lasting longer than two minutes. Ably preserves [connection state](/docs/connect/states#connection-state-recovery) for up to two minutes. Beyond this window, Ably cannot guarantee message continuity.
+- Server-initiated continuity loss. Operational events such as cluster rebalancing may cause a partial loss of message continuity, even if the client remained connected.
+- Outbound rate limits exceeded. If a connection's outbound message rate exceeds the [per-connection limit](/docs/platform/pricing/limits#connection), messages may be dropped, resulting in a loss of continuity.
+- Client app backgrounded for an extended period. Mobile apps suspended by the operating system may exceed the two-minute recovery window.
+
+For disconnections shorter than two minutes, the SDK automatically [resumes](/docs/connect/states#resume) the connection and replays missed messages without any action from you.
+
+## Detect discontinuity
+
+The [Chat SDK](/docs/chat) provides an `onDiscontinuity()` handler at the room level. This is a Chat-specific mechanism, separate from the Pub/Sub `resumed` flag.
+
+
+
+Register the handler when setting up your room:
+
+
+```javascript
+const { off } = room.onDiscontinuity((reason) => {
+ console.log('Discontinuity detected:', reason);
+ recoverChatMessages(room);
+});
+
+// Clean up when done
+off();
+```
+
+```react
+import { useMessages } from '@ably/chat/react';
+
+const MyComponent = () => {
+ useMessages({
+ onDiscontinuity: (error) => {
+ console.log('Discontinuity detected:', error);
+ // Trigger recovery, for example re-fetch message history
+ },
+ });
+
+ return
...
;
+};
+```
+
+```swift
+let subscription = room.onDiscontinuity()
+for await error in subscription {
+ print("Discontinuity detected: \(error)")
+ // Recover missed messages
+}
+```
+
+```kotlin
+val (off) = room.onDiscontinuity { reason: ErrorInfo ->
+ println("Discontinuity detected: $reason")
+ // Recover missed messages
+}
+
+// Clean up when done
+off()
+```
+
+
+
+
+## Recover missed messages
+
+Use [`historyBeforeSubscribe()`](/docs/chat/rooms/history#subscribe) to retrieve messages from the point of re-subscription. This is preferred over `messages.history()` for discontinuity recovery because the attachment point changes after a resume, and `historyBeforeSubscribe` guarantees no gap between historical and live messages:
+
+
+```javascript
+async function recoverChatMessages(room) {
+ const history = await room.messages.historyBeforeSubscribe({ limit: 50 });
+
+ // Refresh your message list with recovered messages
+ for (const msg of history.items.reverse()) {
+ appendMessageToUI(msg);
+ }
+}
+```
+
+
+
+
+## Best practices
+
+- Set up the `onDiscontinuity` handler before subscribing to messages. This ensures you detect any continuity loss that occurs during the initial attachment.
+- Use `historyBeforeSubscribe()` for recovery. It is designed to work with the Chat discontinuity detection mechanism and guarantees no gap between historical and live messages.
+- The `onDiscontinuity` handler fires at the room level, covering messages, presence, reactions, and typing indicators. You do not need to register separate handlers for each Chat feature.
+- Decide how to present recovered messages to the user. Options include refreshing the message list, showing a "new messages" indicator, or displaying a notification that messages were recovered.
diff --git a/src/pages/docs/guides/pub-sub/handling-discontinuity.mdx b/src/pages/docs/guides/pub-sub/handling-discontinuity.mdx
new file mode 100644
index 0000000000..3022443082
--- /dev/null
+++ b/src/pages/docs/guides/pub-sub/handling-discontinuity.mdx
@@ -0,0 +1,94 @@
+---
+title: "Guide: Handle discontinuity in Pub/Sub"
+meta_description: "Detect and recover from message discontinuity in Ably Pub/Sub applications. Learn to use the resumed flag and history API to recover missed messages."
+meta_keywords: "discontinuity, message continuity, resumed flag, message recovery, missed messages, reconnection, history, untilAttach"
+redirect_from:
+ - /docs/guides/handling-discontinuity
+---
+
+When a client experiences a period of disconnection longer than the two-minute recovery window, or when Ably signals a loss of message continuity, your application may have missed messages. This is called a *discontinuity*. This guide explains how to detect and recover from discontinuities in Pub/Sub applications.
+
+
+
+## What causes discontinuity
+
+Discontinuity occurs when the Ably SDK cannot guarantee that all messages have been delivered to the client. The most common causes are:
+
+- Network disconnection lasting longer than two minutes. Ably preserves [connection state](/docs/connect/states#connection-state-recovery) for up to two minutes. Beyond this window, Ably cannot guarantee message continuity.
+- Server-initiated continuity loss. Operational events such as cluster rebalancing may cause a partial loss of message continuity, even if the client remained connected.
+- Outbound rate limits exceeded. If a connection's outbound message rate exceeds the [per-connection limit](/docs/platform/pricing/limits#connection), messages may be dropped, resulting in a loss of continuity.
+- Client app backgrounded for an extended period. Mobile apps suspended by the operating system may exceed the two-minute recovery window.
+
+For disconnections shorter than two minutes, the SDK automatically [resumes](/docs/connect/states#resume) the connection and replays missed messages without any action from you.
+
+## Detect discontinuity
+
+When continuity is lost on a Pub/Sub channel, the client receives an `ATTACHED` or [`UPDATE`](/docs/channels/states#update) event with the `resumed` flag set to `false`. This flag is part of the [`ChannelStateChange`](/docs/api/realtime-sdk/types#channel-state-change) object.
+
+Register listeners for both `attached` and `update` events to detect all discontinuity scenarios:
+
+
+```javascript
+channel.on('attached', (stateChange) => {
+ if (!stateChange.resumed) {
+ // Continuity was lost - messages may have been missed
+ recoverMissedMessages(channel);
+ }
+});
+
+channel.on('update', (stateChange) => {
+ if (!stateChange.resumed) {
+ // Mid-session continuity loss
+ recoverMissedMessages(channel);
+ }
+});
+```
+
+
+- An `attached` event with `resumed` set to `false` occurs when a channel reattaches after the connection was [suspended](/docs/connect/states#connection-state-recovery), for example after a disconnection longer than two minutes.
+- An `update` event with `resumed` set to `false` occurs when there is a partial loss of continuity on a channel that remains attached, such as after a partially successful [resume](/docs/connect/states#resume).
+
+
+
+## Recover missed messages
+
+Use [`channel.history()`](/docs/storage-history/history#until-attach) with `untilAttach` set to `true` to retrieve messages up to the point of reattachment. The response is paginated, so you may need to iterate through multiple pages. You are responsible for determining how far back to go, for example by using time bounds or checking message serials against the last message you processed:
+
+
+```javascript
+async function recoverMissedMessages(channel, lastSeenSerial) {
+ let page = await channel.history({ untilAttach: true });
+
+ while (page) {
+ for (const msg of page.items) {
+ if (msg.serial <= lastSeenSerial) {
+ // Reached messages already processed
+ return;
+ }
+ processMessage(msg);
+ }
+
+ page = page.hasNext() ? await page.next() : null;
+ }
+}
+```
+
+
+
+
+
+
+## Best practices
+
+- Set up discontinuity handlers before subscribing to messages or attaching to channels. This ensures you detect any continuity loss that occurs during the initial attachment.
+- Use `untilAttach: true` with `channel.history()` for recovery. This is designed to work with the `resumed` flag detection mechanism.
+- History results may overlap with messages already received via the live subscription. Design your message processing to tolerate duplicates, for example by tracking message IDs or serials.
+- Decide how to present recovered messages to the user. Options include silently inserting them into the message list, showing a "new messages" indicator, or displaying a notification that messages were recovered.