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

  1. Strip or rename reserved keys before writing: if (['__proto__', 'constructor', 'prototype'].includes(k)) skip or prefix it
  2. Never build Maps from raw parsed JSON — copy through an allowlist of expected keys
  3. 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

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


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/b5e8e4be3dd283f1. Report an issue: GitHub.