Automattic/mongoose · error · Error
Mongoose maps do not support reserved key name "${key}"
Error message
Mongoose maps do not support reserved key name "${key}" What it means
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.
Source
Thrown at lib/types/map.js:364
* Since maps are stored as objects under the hood, keys must be strings
* and can't contain any invalid characters
* @param {string} key
* @api private
*/
function checkValidKey(key) {
const keyType = typeof key;
if (keyType !== 'string') {
throw new TypeError(`Mongoose maps only support string keys, got ${keyType}`);
}
if (key.startsWith('$')) {
throw new Error(`Mongoose maps do not support keys that start with "$", got "${key}"`);
}
if (key.includes('.')) {
throw new Error(`Mongoose maps do not support keys that contain ".", got "${key}"`);
}
if (specialProperties.has(key)) {
throw new Error(`Mongoose maps do not support reserved key name "${key}"`);
}
}
module.exports = MongooseMap;
View on GitHub (pinned to 49cdab0136)
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
Example fix
// before
for (const [k, v] of Object.entries(req.body)) doc.data.set(k, v);
// after
const reserved = new Set(['__proto__', 'constructor', 'prototype']);
for (const [k, v] of Object.entries(req.body)) {
if (!reserved.has(k)) doc.data.set(k, v);
} Defensive patterns
Strategy: validation
Validate before calling
const RESERVED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
function safeAssignToMap(map, obj) {
for (const [k, v] of Object.entries(obj)) {
if (!RESERVED_KEYS.has(k) && !k.startsWith('$') && !k.includes('.')) map.set(k, v);
}
} Type guard
function isNonReservedKey(key) { return !['__proto__', 'constructor', 'prototype'].includes(key); } Try / catch
try { doc.data.set(k, v); } catch (err) { if (/reserved key name/.test(err.message)) log.warn(`Blocked reserved key: ${k}`); else throw err; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: Security-sensitive endpoints that persist arbitrary user-supplied keys; merging request bodies into documents without key filtering.
Related errors
- Mongoose maps only support string keys, got ${keyType}
- Mongoose maps do not support keys that start with "$", got "
- Mongoose maps do not support keys that contain ".", got "${k
- Must provide `autoEncryption` when connecting with encrypted
- ${key} is not allowed with sanitizeFilter
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/b5e8e4be3dd283f1.
Report an issue: GitHub.