Automattic/mongoose · error · StrictModeError
Field ${fullPath} is immutable and strict = 'throw'
Error message
Field ${fullPath} is immutable and strict = 'throw' What it means
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.
Source
Thrown at lib/helpers/query/handleImmutable.js:38
return false;
}
let immutable = schematype.options.immutable;
if (typeof immutable === 'function') {
immutable = immutable.call(ctx, ctx);
}
if (!immutable) {
return false;
}
if (options?.overwriteImmutable) {
return false;
}
if (strict === false) {
return false;
}
if (strict === 'throw') {
throw new StrictModeError(null,
`Field ${fullPath} is immutable and strict = 'throw'`);
}
delete obj[key];
return true;
};
View on GitHub (pinned to 49cdab0136)
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
Example fix
// before
await Model.updateOne({ _id }, { $set: { createdAt: new Date() } }, { strict: 'throw' });
// after (intentional overwrite)
await Model.updateOne({ _id }, { $set: { createdAt: new Date() } }, { strict: 'throw', overwriteImmutable: true }); Defensive patterns
Strategy: validation
Validate before calling
// Remove immutable paths from an update before running it
function stripImmutable(schema, update) {
for (const op of Object.keys(update)) {
if (typeof update[op] !== 'object') continue;
for (const p of Object.keys(update[op])) {
const st = schema.path(p);
if (st?.options?.immutable && op !== '$setOnInsert') delete update[op][p];
}
}
return update;
} Type guard
const isImmutablePath = (schema, path) => Boolean(schema.path(path)?.options?.immutable);
Try / catch
try {
await Model.updateOne(f, u, { strict: 'throw' });
} catch (err) {
if (err instanceof mongoose.Error.StrictModeError) {
// strip the immutable field from the payload, or re-run with { overwriteImmutable: true }
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Field `${pathToCheck}` is not in schema and strict mode is s
- Field `${prefix}${key}` is not in schema and strict mode is
- Path `${path}` is immutable and strict mode is set to throw.
- Field `${path}` is not in schema and strict mode is set to t
- Path "${path}" is not in schema, strict mode is `true`, and
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/4b081539f109e160.
Report an issue: GitHub.