{"record":{"id":"4b081539f109e160","repo":"Automattic/mongoose","slug":"field-fullpath-is-immutable-and-strict-throw","errorCode":null,"errorMessage":"Field ${fullPath} is immutable and strict = 'throw'","messagePattern":"Field (.+?) is immutable and strict = 'throw'","errorType":"exception","errorClass":"StrictModeError","httpStatus":null,"severity":"error","filePath":"lib/helpers/query/handleImmutable.js","lineNumber":38,"sourceCode":"    return false;\n  }\n  let immutable = schematype.options.immutable;\n\n  if (typeof immutable === 'function') {\n    immutable = immutable.call(ctx, ctx);\n  }\n  if (!immutable) {\n    return false;\n  }\n\n  if (options?.overwriteImmutable) {\n    return false;\n  }\n  if (strict === false) {\n    return false;\n  }\n  if (strict === 'throw') {\n    throw new StrictModeError(null,\n      `Field ${fullPath} is immutable and strict = 'throw'`);\n  }\n\n  delete obj[key];\n  return true;\n};\n","sourceCodeStart":20,"sourceCodeEnd":45,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/helpers/query/handleImmutable.js#L20-L45","documentation":"This StrictModeError is thrown from update casting (castUpdate -> handleImmutable) when an update tries to modify a path declared immutable: true while strict is 'throw'. By default Mongoose silently strips immutable paths from updates; strict: 'throw' (schema-level or per-query) turns that stripping into this error. The options.overwriteImmutable: true query option bypasses the check.","triggerScenarios":"Schema has createdAt: { type: Date, immutable: true } and you run Model.updateOne({ _id }, { $set: { createdAt: new Date() } }, { strict: 'throw' }), or the schema/query is configured with strict: 'throw' and the update touches any immutable path. Also fires via bulkWrite updateOne unless its overwriteImmutable is set.","commonSituations":"Data-ingestion or sync jobs that rewrite whole documents including immutable createdAt/owner fields; generic CRUD handlers that $set every submitted field; enabling strict:'throw' globally to harden an app and surfacing previously-silent immutable stripping.","solutions":["Remove the immutable path from the update payload (only update mutable fields)","If the overwrite is intentional, pass { overwriteImmutable: true } as a query/bulkWrite option","Keep strict as true (default) so immutable paths are silently stripped instead of throwing","For upserts, set immutable initial values with $setOnInsert, which is exempt from immutable stripping"],"exampleFix":"// before\nawait Model.updateOne({ _id }, { $set: { createdAt: new Date() } }, { strict: 'throw' });\n\n// after (intentional overwrite)\nawait Model.updateOne({ _id }, { $set: { createdAt: new Date() } }, { strict: 'throw', overwriteImmutable: true });","handlingStrategy":"validation","validationCode":"// Remove immutable paths from an update before running it\nfunction stripImmutable(schema, update) {\n  for (const op of Object.keys(update)) {\n    if (typeof update[op] !== 'object') continue;\n    for (const p of Object.keys(update[op])) {\n      const st = schema.path(p);\n      if (st?.options?.immutable && op !== '$setOnInsert') delete update[op][p];\n    }\n  }\n  return update;\n}","typeGuard":"const isImmutablePath = (schema, path) => Boolean(schema.path(path)?.options?.immutable);","tryCatchPattern":"try {\n  await Model.updateOne(f, u, { strict: 'throw' });\n} catch (err) {\n  if (err instanceof mongoose.Error.StrictModeError) {\n    // strip the immutable field from the payload, or re-run with { overwriteImmutable: true }\n  } else throw err;\n}","preventionTips":["List immutable paths in onboarding docs so API consumers never submit them","Use field whitelists in generic CRUD handlers instead of $set of whole bodies","Reserve strict:'throw' for ops where you want loud failures, and pair it with payload filtering"],"tags":["mongoose","immutable","strict-mode","update"],"backgroundTag":"immutable-field-violation","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}