{"record":{"id":"b333c0f41e72d2f9","repo":"Automattic/mongoose","slug":"document-must-have-an-id-before-saving","errorCode":null,"errorMessage":"document must have an _id before saving","messagePattern":"document must have an _id before saving","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":427,"sourceCode":"\n  let result = null;\n  let where = null;\n  try {\n    const saveOptions = _createSaveOptions(this, options);\n\n    if (this.$isNew) {\n      const hasOnlyPrimitiveValues = this.$__hasOnlyPrimitiveValues();\n      // send entire doc\n      const obj = hasOnlyPrimitiveValues ?\n        this.$__toObjectShallow() :\n        this.toObject(saveToObjectOptions);\n      if ((obj || {})._id === void 0) {\n        // documents must have an _id else mongoose won't know\n        // what to update later if more changes are made. the user\n        // wouldn't know what _id was generated by mongodb either\n        // nor would the ObjectId generated by mongodb necessarily\n        // match the schema definition.\n        throw new MongooseError('document must have an _id before saving');\n      }\n\n      this.$__version(true, obj);\n      this.$__reset();\n      _setIsNew(this, false, hasOnlyPrimitiveValues);\n      // Make it possible to retry the insert\n      this.$__.inserting = true;\n      result = await this[modelCollectionSymbol].insertOne(obj, saveOptions).catch(err => {\n        _setIsNew(this, true, hasOnlyPrimitiveValues);\n        throw err;\n      });\n    } else {\n      // Make sure we don't treat it as a new object on error,\n      // since it already exists\n      this.$__.inserting = false;\n      const pathsToSave = Array.isArray(options.pathsToSave) ? options.pathsToSave : null;\n      const pathsToSaveSet = pathsToSave != null ? new Set(pathsToSave) : null;\n      const delta = this.$__delta(pathsToSave, pathsToSaveSet);","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L409-L445","documentation":"When saving a new document, Mongoose must know its _id so later updates target the right document; if the object sent to insertOne has no _id after serialization, save() throws this MongooseError. It happens when the schema disables or cannot auto-generate _id: `_id: false`, a custom _id type with no default (e.g. String) and no value supplied, or _id explicitly overwritten with undefined.","triggerScenarios":"new Schema({ _id: false, ... }) used for a top-level model then new Model({}).save(); new Schema({ _id: String }) saved without providing the id; doc._id = undefined (or a spread/merge overwriting _id with undefined) before save.","commonSituations":"Subdocument-style schemas (where _id: false is common) copied for top-level models; migrations from SQL expecting DB-side id generation; human-readable slug ids (_id: String) where some creation paths omit the slug.","solutions":["Provide the _id at creation: new User({ _id: new mongoose.Types.ObjectId() }) or the slug value for String _id","Remove `_id: false` from top-level model schemas","Give custom _id types a default: `_id: { type: String, default: () => generateId() }`","Never let spreads/merges overwrite _id with undefined before save"],"exampleFix":"// before\nconst UserSchema = new Schema({ _id: false, name: String });\nconst User = mongoose.model('User', UserSchema);\nawait User.create({ name: 'x' }); // throws\n\n// after\nconst UserSchema = new Schema({ _id: { type: String, default: () => nanoid() }, name: String });\nawait User.create({ name: 'x' });","handlingStrategy":"validation","validationCode":"// Ensure an _id exists before saving a new document\nfunction withId(doc) {\n  if (doc.isNew && doc._id == null) {\n    doc._id = new mongoose.Types.ObjectId();\n  }\n  return doc;\n}\nawait withId(doc).save();","typeGuard":"const hasId = (doc) => doc?._id !== undefined && doc?._id !== null;","tryCatchPattern":"try {\n  await doc.save();\n} catch (err) {\n  if (err instanceof mongoose.MongooseError && /must have an _id/.test(err.message)) {\n    // generate the id (new mongoose.Types.ObjectId() or app-level id) and retry once\n  } else throw err;\n}","preventionTips":["Do not set _id: false on top-level model schemas","Give custom _id types a default generator or require the value at creation","Never assign undefined to _id (watch spreads/merges of user payloads onto documents)"],"tags":["mongoose","save","id","insert","schema"],"backgroundTag":"missing-document-id","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}