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 SDK 4.3.0 or later. Queuing only happens once you enable it and pass a
MessageSentListener. A bare await CometChat.sendMessage(message) with no listener is never queued: offline, it rejects at once, as before.Send a Message
Pass aMessageSentListener to sendMessage. 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 file could not be copied (code PERMANENT), only onError fires.
- TypeScript
- JavaScript
MessageSentListener is accepted by all four send methods:
The returned Promise is not replaced by the listener. It means “was it sent now?”: offline it rejects, and the eventual send is observed through
onMessageSent or getPendingMessages, never the Promise. Always attach a .catch() when you rely on the listener alone.Queued, Not Failed
When the device is offline, the send still firesonMessagePending, then fires onError once with the code FAILED_TO_FETCH. This is a notification, not a failure: the message stays durably PENDING, and onMessageSent fires later, on reconnect, when it actually goes. Do not re-send on that error. 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. Read the message’s status to tell the two apart — keep the localMessageId from onMessagePending, then look the message up in the outbox:
- TypeScript
- JavaScript
onError, and its Promise does not reject. It resolves, with onMessageSent, when it is sent.
Message status and failure reason
Every message exposes its offline state.getLocalMessageId() returns 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. Pass a conversation id to limit it to one conversation. Read each message’s status to group them.
- TypeScript
- JavaScript
message.files). With offline off, or nobody signed in, it resolves with an empty list. If the local store is briefly unavailable (busy, locked, full, or still reopening), it rejects with ERROR_OFFLINE_MESSAGE_PERSIST_FAILED instead of returning an empty list — try again.
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 a restart — register a global OfflineMessageListener. Its failed callback carries the MessageFailureReason.
- TypeScript
- JavaScript
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. Each method accepts a message or its localMessageId.
retryMessage(message, listener?)re-attempts one message. AFAILEDmessage is reset toPENDINGand sent; aPENDINGmessage is attempted now rather than waiting for a reconnect. The optionalMessageSentListenerand the returned Promise receive that attempt’s outcome. With no answer, it fires the not-connected notice once (FAILED_TO_FETCH) and the message staysPENDING.retryAllFailedMessages(listener?)retries transient failures only; permanent ones are skipped.discardLocalMessage(message, listener?)deletes an unsent (PENDINGorFAILED) message from the device, including its copied media. The optional listener is{ onSuccess, onError };onSuccessreceives thelocalMessageId.
- TypeScript
- JavaScript
maxPendingMessages.
retryMessage and discardLocalMessage reject, changing nothing, when:
Auto-send on Reconnect
By default, queuedPENDING messages are sent automatically when the connection is re-established. This is controlled by autoSendPendingOnReconnect (default true); see Enable Offline Support and the reference. With it off, a send that gets no answer fails at once with reason TRANSIENT (its listener gets one onError with FAILED_TO_FETCH and the message is FAILED), which you recover with retryMessage.
A send that gets no answer for 15 seconds (media: 40 seconds plus upload time) counts as unanswered. After five reconnect attempts with no answer it fails with TRANSIENT.
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, pendingQueueOverflowPolicy decides what happens:
A media send with a file larger than
maxMediaCacheSize is refused with reason BYTE_OVERFLOW; nothing is evicted to make room.
A refused send (QUEUE_FULL / BYTE_OVERFLOW) is never queued, so it fires no onMessagePending; it reports onError, and the global OfflineMessageListener receives onOutboxMessageFailed(message, reason) carrying the exact reason. For QUEUE_FULL the error’s code is QUEUE_FULL, with details.maxPendingMessages.
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 fail with the not-connected error if the request gets no answer.Durable Media Uploads
A multi-attachment upload survives a restart: the SDK re-attaches to an in-progress batch instead of restarting it. Fetch the existing request for a conversation withgetExistingUploadFileRequest (it resolves null when there is none, or offline is off), read each file’s progress with getItems(), and retry an individual file with retryAttachment(fileId). Uploads never auto-resume: restored files come back retryable and wait for the user. A FAILED file with localCopyMissing: true cannot be retried — ask the user to re-add it.
- TypeScript
- JavaScript