{"record":{"id":"e2b32618ede10baa","repo":"Budibase/budibase","slug":"unable-to-remove-doc-without-a-valid-id-and-rev","errorCode":null,"errorMessage":"Unable to remove doc without a valid _id and _rev.","messagePattern":"Unable to remove doc without a valid _id and _rev\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/backend-core/src/db/couch/DatabaseImpl.ts","lineNumber":282,"sourceCode":"    return rows.map(row => (includeDocs ? row.doc! : row.value))\n  }\n\n  async remove(idOrDoc: string | Document, rev?: string) {\n    // not a read call - but don't create a DB to delete a document\n    return this.performCall(db => {\n      let _id: string\n      let _rev: string\n\n      if (isDocument(idOrDoc)) {\n        _id = idOrDoc._id!\n        _rev = idOrDoc._rev!\n      } else {\n        _id = idOrDoc\n        _rev = rev!\n      }\n\n      if (!_id || !_rev) {\n        throw new Error(\"Unable to remove doc without a valid _id and _rev.\")\n      }\n      return () => db.destroy(_id, _rev)\n    })\n  }\n\n  async bulkRemove(documents: Document[], opts?: { silenceErrors?: boolean }) {\n    const response: Nano.DocumentBulkResponse[] = await this.performCall(db => {\n      return () =>\n        db.bulk({\n          docs: documents.map(doc => ({\n            ...doc,\n            _deleted: true,\n          })),\n        })\n    })\n    if (opts?.silenceErrors) {\n      return\n    }","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/db/couch/DatabaseImpl.ts#L264-L300","documentation":"remove deletes a document by _id and _rev; CouchDB requires both. When called with an object lacking _id/_rev or an id string without a rev, the wrapper refuses to issue the delete and throws, avoiding guaranteed CouchDB failures.","triggerScenarios":"remove(doc) where doc has no _rev (fetched without revs or constructed in memory), or remove(id) without the rev argument (undefined/null rev).","commonSituations":"Docs built client-side without revs, using stale objects whose _rev was stripped by serialization, forgetting to pass rev when calling remove(id, rev).","solutions":["Fetch the doc first (db.get) to obtain _id and _rev, then remove it","Pass the rev explicitly: db.remove(id, rev)","Validate the doc shape before calling remove"],"exampleFix":"// before\nawait db.remove(doc) // doc may lack _rev\n// after\nconst existing = await db.get(doc._id)\nawait db.remove(existing._id, existing._rev)","handlingStrategy":"validation","validationCode":"const canRemove = (doc: Document): doc is Document & { _id: string; _rev: string } =>\n  typeof doc._id === \"string\" && typeof doc._rev === \"string\"\nif (!canRemove(doc)) throw new Error(\"doc needs _id and _rev before remove\")\nawait db.remove(doc)","typeGuard":"const isRemovable = (d: Document): d is Document & { _id: string; _rev: string } =>\n  !!(d as any)._id && !!(d as any)._rev","tryCatchPattern":"try {\n  await db.remove(doc)\n} catch (err) {\n  if (err.message.includes(\"without a valid _id and _rev\")) {\n    const existing = await db.get(doc._id)\n    await db.remove(existing)\n  } else throw err\n}","preventionTips":["Always db.get a doc before removing to guarantee a fresh _rev","Avoid round-tripping docs through serialization that drops _rev","Prefer remove(id, rev) with explicit arguments"],"tags":["couchdb","validation","delete"],"backgroundTag":"missing-document-rev","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}