{"record":{"id":"b5e8e4be3dd283f1","repo":"Automattic/mongoose","slug":"mongoose-maps-do-not-support-reserved-key-name","errorCode":null,"errorMessage":"Mongoose maps do not support reserved key name \"${key}\"","messagePattern":"Mongoose maps do not support reserved key name \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/types/map.js","lineNumber":364,"sourceCode":" * Since maps are stored as objects under the hood, keys must be strings\n * and can't contain any invalid characters\n * @param {string} key\n * @api private\n */\n\nfunction checkValidKey(key) {\n  const keyType = typeof key;\n  if (keyType !== 'string') {\n    throw new TypeError(`Mongoose maps only support string keys, got ${keyType}`);\n  }\n  if (key.startsWith('$')) {\n    throw new Error(`Mongoose maps do not support keys that start with \"$\", got \"${key}\"`);\n  }\n  if (key.includes('.')) {\n    throw new Error(`Mongoose maps do not support keys that contain \".\", got \"${key}\"`);\n  }\n  if (specialProperties.has(key)) {\n    throw new Error(`Mongoose maps do not support reserved key name \"${key}\"`);\n  }\n}\n\nmodule.exports = MongooseMap;\n","sourceCodeStart":346,"sourceCodeEnd":369,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/types/map.js#L346-L369","documentation":"checkValidKey() rejects the reserved key names '__proto__', 'constructor', and 'prototype' (the specialProperties set). These control JavaScript prototype mechanics; writing them into the object-backed Map would enable prototype pollution, so Mongoose blocks them outright.","triggerScenarios":"doc.map.set('__proto__', v); deserializing untrusted JSON directly into a Map field via Object.assign or map construction; user input that includes 'constructor' as a key.","commonSituations":"Security-sensitive endpoints that persist arbitrary user-supplied keys; merging request bodies into documents without key filtering.","solutions":["Strip or rename reserved keys before writing: if (['__proto__', 'constructor', 'prototype'].includes(k)) skip or prefix it","Never build Maps from raw parsed JSON — copy through an allowlist of expected keys","Reject such keys at the API boundary (400) when they come from clients"],"exampleFix":"// before\nfor (const [k, v] of Object.entries(req.body)) doc.data.set(k, v);\n// after\nconst reserved = new Set(['__proto__', 'constructor', 'prototype']);\nfor (const [k, v] of Object.entries(req.body)) {\n  if (!reserved.has(k)) doc.data.set(k, v);\n}","handlingStrategy":"validation","validationCode":"const RESERVED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\nfunction safeAssignToMap(map, obj) {\n  for (const [k, v] of Object.entries(obj)) {\n    if (!RESERVED_KEYS.has(k) && !k.startsWith('$') && !k.includes('.')) map.set(k, v);\n  }\n}","typeGuard":"function isNonReservedKey(key) { return !['__proto__', 'constructor', 'prototype'].includes(key); }","tryCatchPattern":"try { doc.data.set(k, v); } catch (err) { if (/reserved key name/.test(err.message)) log.warn(`Blocked reserved key: ${k}`); else throw err; }","preventionTips":["Copy untrusted objects through key allowlists — never Object.assign into Map-backed fields","Treat this guard as a prototype-pollution defense line; keep it enabled","Add security tests that post __proto__/constructor keys against your endpoints"],"tags":["mongoose","map","keys","prototype-pollution","security"],"backgroundTag":"invalid-map-key","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}