Offline Queue
How Mite retries bug reports that fail because of network conditions.
When a bug report fails because of the network — not because the server rejected it — Mite holds onto it and retries in the background. A user on a train filing a report does not lose it.
The queue is enabled by default and set up by mite.init().
const mite = new Mite({
apiKey: process.env.EXPO_PUBLIC_MITE_API_KEY,
enableOfflineQueue: true, // default
})
mite.init()What gets queued
Only bug reports, and only on a genuine network failure — an axios error with
code ERR_NETWORK, ECONNABORTED, or ETIMEDOUT. A 4xx or 5xx response
means the server was reached and answered, so the report is not queued.
Feature requests, votes, and identify calls are not queued.
A plan quota refusal is never queued and never retried, at any level of the SDK. The result cannot change until the plan changes or the billing period ends, so a queued report would only grow the queue. An entry that meets a 402 during a flush is dropped at once instead of counting against its retries.
submitBug still rejects after queuing. Queuing is a background safety net,
not a silent success — your UI decides what to tell the user. Check
mite.pendingRequestCount to distinguish "saved for later" from "lost".
try {
const result = await mite.submitBug({ title, description })
showToast(result.ok ? 'Thanks for the report!' : 'Reports are not being accepted right now.')
} catch {
showToast(
mite.pendingRequestCount > 0
? "Saved — we'll send it when you're back online."
: 'Could not send your report.',
)
}Attachments are dropped
Uploads cannot be replayed from the queue, so attachments are stripped from a
queued report and a [Mite] warning is logged. The report text, device info,
and navigation trail are all preserved — only the files are lost.
Retry behavior
| Behavior | Value |
|---|---|
| Flush interval | Every 30 seconds while the queue is non-empty |
| Max attempts per request | 5, after which it is dropped with a [Mite] error log |
| Max queue size | 100 requests — the oldest is evicted when full |
| Max age | 24 hours, after which a request is discarded on the next flush |
The flush timer starts when the first request is queued and stops once the queue drains, so an idle app does not hold a timer.
The queue is in memory only. It does not survive an app restart — a pending report is lost if the user kills the app before it flushes. It covers transient connectivity blips, not extended offline sessions.
Inspecting and flushing
mite.pendingRequestCount // number of queued requests
await mite.flushOfflineQueue() // retry now, e.g. on reconnectflushOfflineQueue() resolves once the attempt finishes and never rejects;
individual failures stay queued for the next round. Concurrent calls share a
single in-flight flush.
Pair it with a connectivity listener to retry the moment the network returns instead of waiting for the next tick:
import NetInfo from '@react-native-community/netinfo'
NetInfo.addEventListener(state => {
if (state.isConnected) {
void mite.flushOfflineQueue()
}
})Disabling
const mite = new Mite({
apiKey: process.env.EXPO_PUBLIC_MITE_API_KEY,
enableOfflineQueue: false,
})Failed reports then simply throw, and it is up to you to retry.
Relationship to retries
These are two separate mechanisms and they compose:
retries(default0) retries the HTTP request in place, with exponential backoff capped at 10 seconds, beforesubmitBugever rejects.- The offline queue takes over after all of that has failed, retrying on a 30-second timer.
Teardown
mite.destroy()Stops the flush timer and clears the queue, discarding anything pending.