pubkey/rxdb · error · RxError
CONFLICT
CONFLICT
Error message
RxDB Error-Code: ${message}.
Hint: Error messages are not included in RxDB core to reduce build size.
To show the full error messages and to ensure that you do not make any mistakes when using RxDB,
use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error
Find out more about this error here: https://rxdb.info/errors.html?console=errors#CONFLICT
Still stuck? Ask in the RxDB Discord: https://rxdb.info/chat
What it means
CONFLICT is thrown by throwIfIsStorageWriteError() when the underlying RxStorage returns a write error with HTTP-style status 409, meaning the write expected a document revision that no longer matches the currently stored one. This is RxDB's optimistic-concurrency check: another writer (another tab, device, or replication) changed the document between your read and your write. Callers include insert (already exists), upsert, _saveData and remove (deleted or changed in the meantime).
Source
Thrown at src/rx-storage-helper.ts:167
*/
export function stackCheckpoints<CheckpointType>(
checkpoints: (CheckpointType | undefined)[]
): CheckpointType {
return Object.assign(
{},
...checkpoints.filter(x => !!x)
);
}
export function throwIfIsStorageWriteError<RxDocType>(
collection: RxCollection<RxDocType, any, any>,
documentId: string,
writeData: RxDocumentWriteData<RxDocType> | RxDocType,
error: RxStorageWriteError<RxDocType> | undefined
) {
if (error) {
if (error.status === 409) {
throw newRxError('CONFLICT', {
collection: collection.name,
id: documentId,
writeError: error,
data: writeData
});
} else if (error.status === 422) {
throw newRxError('VD2', {
collection: collection.name,
id: documentId,
writeError: error,
data: writeData
});
} else {
throw error;
}
}
}
View on GitHub (pinned to af6fb65f94)
Solutions
- Retry the operation: re-fetch the document, rebase your change on the new state, and write again (this is what incrementalUpsert/retry loops do).
- Use incrementalUpsert()/incrementalModify() instead of manual upsert so RxDB retries conflicts for you.
- For duplicate inserts, check existence first with collection.findOne(id).exec() or switch to upsert semantics intentionally.
- Handle the CONFLICT RxError in a catch block with a bounded retry and conflict-resolution logic, especially inside replication handlers.
- Ensure leader election is used so only one tab writes at a time.
Example fix
// before
await collection.upsert(doc); // may throw CONFLICT on revision mismatch
// after
import { firstValueFrom } from 'rxjs';
try {
await collection.upsert(doc);
} catch (err) {
if (err.code === 'CONFLICT') {
const current = await collection.findOne(doc.id).exec();
await collection.upsert({ ...doc, _rev: current?.revision });
} else {
throw err;
}
} Defensive patterns
Strategy: retry
Validate before calling
async function docExists(collection: RxCollection, id: string): Promise<boolean> {
return (await collection.findOne(id).exec()) !== null;
}
// before insert: if (await docExists(collection, id)) use upsert or abort; Type guard
function isConflictError(err: unknown): err is RxError {
return err instanceof RxError && (err as any).code === 'CONFLICT';
} Try / catch
import { RxError } from 'rxdb';
let attempts = 0;
while (true) {
try {
await collection.upsert(data);
break;
} catch (err) {
if (isConflictError(err) && ++attempts < 5) {
const current = await collection.findOne(data.id).exec();
data = { ...data, _rev: current?.revision } as typeof data;
continue; // rebase and retry
}
throw err;
}
} Prevention
- Prefer incrementalUpsert()/incrementalModify() which resolve conflicts internally.
- Enable leader election when multiple tabs share the same database.
- In replication conflict handlers, always resolve by merging instead of blindly overwriting.
- Check for an existing document before insert, or use upsert when overwrite is intended.
- Keep write transactions short to shrink the window between read and write.
When it happens
Trigger: collection.insert() when a document with the same primary key already exists; doc.remove() or save when the document was modified/deleted elsewhere since it was read; incrementalUpsert racing with a remote replication write; two tabs writing the same document without leader election.
Common situations: Multi-tab or multi-device usage where two clients edit the same document concurrently; replication pipelines applying remote changes between a local read and write; re-running a seed script that inserts existing ids; calling upsert expecting overwrite semantics when the storage layer still enforces revision checks.
AI-assisted analysis of pubkey/rxdb@af6fb65f94 (2026-08-31).
Data as JSON: /api/errors/e7be4d735743bb70.
Report an issue: GitHub.