Budibase/budibase · error
Cannot store document without _id field.
Error message
Cannot store document without _id field.
What it means
put() in DatabaseImpl requires every stored document to carry an _id field; calling put with an _id-less document throws immediately. Documents must be created without a caller-chosen id via post(), which generates one. Timestamps (createdAt/updatedAt) are also stamped here before persistence.
Source
Thrown at packages/backend-core/src/db/couch/DatabaseImpl.ts:326
}
if (errorFound) {
throw new CouchDBError(errorMessage, {
name: this.name,
status: 400,
})
}
}
async post(document: AnyDocument, opts?: DatabasePutOpts) {
if (!document._id) {
document._id = newid()
}
return this.put(document, opts)
}
async put(document: AnyDocument, opts?: DatabasePutOpts) {
if (!document._id) {
throw new Error("Cannot store document without _id field.")
}
return this.performCallWithDBCreation(async db => {
if (!document.createdAt) {
document.createdAt = new Date().toISOString()
}
document.updatedAt = new Date().toISOString()
if (opts?.force && document._id) {
try {
const existing = await this.get(document._id)
if (existing) {
document._rev = existing._rev
}
} catch (err: any) {
if (err.status !== 404) {
throw err
}
}
}View on GitHub (pinned to a81a902e9a)
Solutions
- Use db.post(doc) instead of put when creating a new document without an id
- Ensure the document object includes _id before calling put (e.g. from a fetched row)
- Add a validation/normalization step that rejects or rejects-with-default _id payloads
- If an id should be derived, set it explicitly: doc._id = doc._id ?? generateId()
Example fix
// before
await db.put({ name: "row1" })
// after
await db.post({ name: "row1" }) // or await db.put({ ...doc, _id: doc._id ?? newid() }) Defensive patterns
Strategy: validation
Validate before calling
if (!doc._id) throw new Error("Document requires _id before put(); use post() to create") Type guard
function hasId(doc: Record<string, unknown>): doc is Record<string, unknown> & { _id: string } {
return typeof doc._id === "string" && doc._id.length > 0
} Prevention
- Use post() for new documents, put() only for updates
- Normalize API payloads to preserve underscore-prefixed CouchDB fields
- Assert doc._id presence in a shared persist helper
When it happens
Trigger: Calling db.put({...}) on an object lacking _id — e.g. building a doc from scratch, spreading a partial payload, or destructuring that drops the _id field.
Common situations: Mistakenly using put instead of post for new documents; constructing docs in migrations/scripts without copying _id; API payloads that omit _id; JSON.parse of a list that stripped the underscore-prefixed key.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Unable to remove doc without a valid _id and _rev.
- Query ID or Revision is missing
- Config name is required
- Config ID is required for updates
- Revision is required for updates
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/3bb7f158be29eb11.
Report an issue: GitHub.