-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Description
The Supabase replication plugin throws an error when pushing a deletion for a document that doesn't exist on the server. This happens when a document is created locally, then deleted before it syncs to Supabase.
Environment
- RxDB Version: 16.21.1
- Plugin:
rxdb/plugins/replication-supabase - Platform: Overwolf desktop app (Chromium-based)
Steps to Reproduce
- Create a document locally (e.g., while offline or before sync starts)
- Delete the document with
doc.remove()before it syncs to Supabase - Observe replication error
Current Behavior
The replication throws:
Error: doc not found <document-id>
With error code RC_PUSH and the push fails repeatedly.
Expected Behavior
Deleting a document that doesn't exist on the server should succeed silently. The desired end state is achieved (document doesn't exist on server), so this should not be treated as an error.
Root Cause
In updateOrReturnConflict(), when the UPDATE returns 0 rows (document doesn't exist), the code calls fetchById() to get the conflict state:
if (data && data.length > 0) {
return;
} else {
// no match -> conflict
return await fetchById(id);
}fetchById() throws when the document doesn't exist:
if (data.length != 1) throw new Error('doc not found ' + id);For deletions (_deleted: true), a non-existent document is the desired end state, not a conflict.
Suggested Fix
Wrap the fetchById call in updateOrReturnConflict to handle the "doc not found" case for deletions:
} else {
// no match -> check if it's a conflict or if doc simply doesn't exist
try {
return await fetchById(id);
} catch (err) {
// If document doesn't exist and we're trying to delete it,
// the desired state is achieved - not a conflict
if (doc._deleted && err.message && err.message.includes('doc not found')) {
return undefined;
}
throw err;
}
}Workaround
In the application's error handler, ignore "doc not found" errors for push operations where _deleted: true:
replication.error$.subscribe((error) => {
const isDocNotFoundOnPush =
error.message?.includes('doc not found') &&
error.direction === 'push';
if (isDocNotFoundOnPush) {
const isDeleteOperation = error.parameters?.pushRows?.some(
(row) => row.newDocumentState?._deleted === true
);
if (isDeleteOperation) {
// Ignore - document doesn't exist on server, which is the desired state
return;
}
}
// Handle other errors...
});Note: This workaround only suppresses the error display; RxDB may still retry the failed push internally.
Suggested diff
The fix is in src/plugins/replication-supabase/index.ts. Here's the diff:
--- a/src/plugins/replication-supabase/index.ts
+++ b/src/plugins/replication-supabase/index.ts
@@ -139,7 +139,7 @@ export function replicateSupabase<RxDocType>(
async function updateOrReturnConflict(
doc: WithDeleted<RxDocType>,
assumedMasterState: WithDeleted<RxDocType>
- ): Promise<WithDeleted<RxDocType> | undefined> {
+ ): Promise<WithDeleted<RxDocType> | undefined> {
ensureNotFalsy(assumedMasterState);
const id = (doc as any)[primaryPath];
const toRow = flatClone(doc);
@@ -161,7 +161,18 @@ export function replicateSupabase<RxDocType>(
if (data && data.length > 0) {
return;
} else {
- // no match -> conflict
- return await fetchById(id);
+ // no match -> check if it's a conflict or if doc simply doesn't exist
+ try {
+ return await fetchById(id);
+ } catch (err: any) {
+ // If document doesn't exist and we're trying to delete it,
+ // the desired state is achieved - treat as success, not conflict
+ if (doc._deleted && err.message && err.message.includes('doc not found')) {
+ return undefined;
+ }
+ throw err;
+ }
}
}