AI Integration Quick Reference
AI Integration Quick Reference
autoSendPendingOnReconnect on, the default). This page shows the offline-aware send listener, how to read a message’s status, how to inspect and recover the send queue, and the queue limits.
Offline support is off by default and requires
cometchat_sdk 5.1.0 or newer. Queuing only happens once you enable it; with offline off, a send over no connection fails exactly as before.Starting with v5.1.0, `sendMessage()`, `sendMediaMessage()`, `sendCustomMessage()` and `sendInteractiveMessage()` accepts a
listener: parameter that takes a MessageSentListener. The listener: parameter requires cometchat_sdk 5.1.0 or later. With offline support off, the listener behaves exactly like the old callback: the sent message arrives in onMessageSent (the drop-in for onSuccess) and onError is unchanged. With offline support on, onMessagePending fires first — the message is persisted locally. A message sent without a connection stays pending and is sent when the connection returns: onError fires once with code ERR_CONNECTION (details ERROR_INTERNET_UNAVAILABLE) as a not-connected notice, and onMessageSent follows once the message is sent. In onError, code ERR_CONNECTION means the message is queued; a real failure also reaches OfflineMessageListener.onOutboxMessageFailed, with the message’s status set to failed. A send without a listener: is never queued. See Send While Offline.Send a Message
Pass aMessageSentListener to sendMessage through the listener: parameter. Its lifecycle is normally onMessagePending first (the message is durably queued), then its outcome: onMessageSent (the server accepted it) or onError. Offline, onError is a queued-not-failed notice and onMessageSent still fires later on reconnect, so both can fire for one send (see below). If the message cannot be queued at all, for example because its media could not be copied (code ERROR_OUTBOX_MEDIA_COPY_FAILED, reason permanent), only onError fires.
- Dart
listener: returns a Future: it resolves with the server-acked message once the send is answered, and rejects (throws) when the send is attempted but gets no answer — the row stays durably pending and onMessageSent fires on a later reconnect. A send without a listener: is a direct send and is never queued.
The same MessageSentListener is accepted by all four persistent send methods:
- Dart
Queued, Not Failed
When the device is offline, the send still firesonMessagePending, then fires onError with the code ERR_CONNECTION (details ERROR_INTERNET_UNAVAILABLE) for that attempt. This is a notification, not a failure: the message stays durably pending, and onMessageSent fires later, on reconnect, when it actually goes. This is the default behaviour, with autoSendPendingOnReconnect on. With it off, an offline send becomes failed (transient) right away, and you recover it with retryMessage.
So for a single send, onError and onMessageSent can both fire (offline, then reconnect), and onError does not always mean the message failed. Tell the two apart like this:
Every failure reaches
onOutboxMessageFailed, whatever its cause. Match it to the message in your UI by localMessageId: the SDK sets it on the message you passed to sendMessage before onMessagePending fires. The status of that passed-in object is not updated; read status from the messages the SDK delivers (onMessagePending, onOutboxMessageFailed, getPendingMessages()).
- Dart
Message status and failure reason
Every message exposes its offline state.localMessageId is the SDK-generated handle that is stamped at compose (while offline is running) and stays stable across the whole lifecycle — use it to retry or discard the message.
MessageFailureReason values:
Inspect the Queue
getPendingMessages returns the entire un-acked outbox — every pending, sending, and failed message, ordered by local compose time. Sent messages are not included. Read each message’s status to group them.
- Dart
Observe the queue app-wide
The per-callMessageSentListener lives only as long as the call. To watch every queue transition — a live send, a reconnect auto-flush, a manual retry, or a resumed send after an app restart — register a global OfflineMessageListener. Its failed callback carries the MessageFailureReason.
- Dart
Retry and Discard
There is no automatic retry of afailed message — recovery is explicit and user-invoked. A failed message stays in the outbox until you retry or discard it. Use the message’s localMessageId.
retryMessage(localMessageId, listener: ...)re-attempts one message. Afailedmessage is reset topendingand sent; apendingmessage is attempted now rather than waiting for a reconnect. The listener receives that attempt’s outcome.retryAllFailedMessages()retries transient failures only; permanent ones are skipped. It takes no listener — outcomes arrive on the globalOfflineMessageListener.discardLocalMessage(localMessageId, onSuccess: ..., onError: ...)deletes an unsent (pendingorfailed) message from the device, including its copied media.onSuccessreceives thelocalMessageId.
- Dart
retryMessage and discardLocalMessage reject, changing nothing, when:
Auto-send on Reconnect
By default, queuedpending messages are sent automatically when the realtime connection is re-established. This is controlled by OfflineConfig.autoSendPendingOnReconnect (default true); see Enable Offline Support and the reference. A queued message that keeps getting no answer is retried on each reconnect, up to 5 times, then becomes failed (transient). With it off, nothing retries a queued message on reconnect — a send that gets no answer becomes failed (transient), which you recover with retryMessage.
Reconnect also runs a catch-up sync for received messages — see Stay in Sync.
Queue Limits
The outbox holds at mostmaxPendingMessages messages still trying to send (pending + sending); failed messages do not count. When the queue is full, OfflineConfig.pendingQueueOverflowPolicy decides what happens:
A count-based refusal or eviction is reported with code
ERROR_OUTBOX_OVERFLOW (the exact reason is in the exception details). A media send whose bytes would push the media cache over maxMediaCacheSize is refused with reason byteOverflow and code ERROR_OUTBOX_BYTE_OVERFLOW; nothing is evicted to make room.
A refused send (queueFull / byteOverflow) is never queued, so it fires no onMessagePending; it reports onError, and the global OfflineMessageListener receives onOutboxMessageFailed(message, reason) carrying the exact reason.
Edits, Deletes, and Reactions Need a Connection
Offline queuing applies to sending new messages only. Changes to existing messages — edit, delete, add or remove a reaction — and other own actions are not queued offline. They are sent to the server whenever you trigger them, and they report the not-connected error if the request gets no answer. Their effect reaches the cache only once the server accepts them.Durable Media Uploads
A multi-attachment upload survives an app restart, but it never resumes on its own. Fetch the existing request for a conversation withgetExistingUploadFileRequest, and read each file’s progress through getFiles(), which returns a list of UploadFileView (each with a state: queued, requestingUrl, uploading, uploaded, attached, cancelled, failed, or rejected, which cannot be retried). Retry an individual file with retryAttachment(fileId), and observe progress by registering an UploadFileListener with addUploadListener.
- Dart