Offline queue prevents duplicate submissions on reconnect
Why it matters
A reconnect that fires while a previous sync is still in progress can submit the same queued action twice — a duplicate order, a duplicate payment, or a duplicate form entry. CWE-362 (Race Condition) and CWE-694 (Use of Multiple Primitives to Lock a Resource) both apply to queue processing without deduplication guards. Without idempotency keys, network flicker during sync transforms a reliability feature into a correctness hazard. On APIs that charge per-request or trigger side effects (emails, webhooks, charges), duplicates have direct financial and trust consequences.
Severity rationale
High because duplicate submissions during reconnect can trigger double charges, double emails, or duplicate records — failures that are difficult to reverse and immediately visible to users.
Remediation
Stamp each queued action with an idempotency key at enqueue time, then send it as a header on every retry. Mark items as synced rather than deleting them immediately, so a mid-sync crash doesn't re-enqueue an already-completed action.
type QueuedAction = {
idempotencyKey: string;
action: unknown;
synced: boolean;
};
export async function enqueue(action: { type: string; payload: { id: string } }) {
const raw = await AsyncStorage.getItem(QUEUE_KEY);
const queue: QueuedAction[] = raw ? JSON.parse(raw) : [];
queue.push({
idempotencyKey: `${action.type}__${action.payload.id}__${Date.now()}`,
action,
synced: false,
});
await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue));
}
export async function processQueue() {
const raw = await AsyncStorage.getItem(QUEUE_KEY);
if (!raw) return;
const queue: QueuedAction[] = JSON.parse(raw);
for (const item of queue) {
if (item.synced) continue;
const res = await fetch('/api/action', {
method: 'POST',
headers: { 'Idempotency-Key': item.idempotencyKey, 'Content-Type': 'application/json' },
body: JSON.stringify(item.action),
});
if (res.ok) item.synced = true;
}
await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue.filter(i => !i.synced)));
}
Your server must also honor the Idempotency-Key header and return the original response on replay.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Mobile Offline & Storage Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-362 — Concurrent Execution Using Shared Resource with Improper Synchronization (Race Condition)
- cwe · CWE-694 — Use of Multiple Resources with Duplicate Identifier
Taxons
History
- 2026-04-18·v1.0.0·Initial import from mobile-offline-storage·automated