MiteSDK

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

BehaviorValue
Flush intervalEvery 30 seconds while the queue is non-empty
Max attempts per request5, after which it is dropped with a [Mite] error log
Max queue size100 requests — the oldest is evicted when full
Max age24 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 reconnect

flushOfflineQueue() 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 (default 0) retries the HTTP request in place, with exponential backoff capped at 10 seconds, before submitBug ever 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.

On this page