Automattic/mongoose · error · MongooseError
Cannot use schema-level projections (`select: true` or `sele
Error message
Cannot use schema-level projections (`select: true` or `select: false`) within maps at path "${path}.${subpath}" What it means
When interpreting a Map of subdocuments, Mongoose walks every path of the value schema and throws if any path declares an explicit `select: true` or `select: false`. Projection options on subpaths inside a Map are unsupported (projections are resolved per top-level path and Map subpaths do not map cleanly onto projection documents), so Mongoose refuses the schema at definition time rather than silently ignoring the option.
Source
Thrown at lib/schema/map.js:182
const mapPath = path + '.$*';
let _mapType = { type: {} };
if (utils.hasUserDefinedProperty(obj, 'of')) {
const isInlineSchema = utils.isPOJO(obj.of) &&
utils.hasOwnKeys(obj.of) &&
!utils.hasUserDefinedProperty(obj.of, schema.options.typeKey);
if (isInlineSchema) {
_mapType = { [schema.options.typeKey]: new Schema(obj.of) };
} else if (utils.isPOJO(obj.of)) {
_mapType = Object.assign({}, obj.of);
} else {
_mapType = { [schema.options.typeKey]: obj.of };
}
if (_mapType[schema.options.typeKey] && _mapType[schema.options.typeKey].instanceOfSchema) {
const subdocumentSchema = _mapType[schema.options.typeKey];
subdocumentSchema.eachPath((subpath, type) => {
if (type.options.select === true || type.options.select === false) {
throw new MongooseError('Cannot use schema-level projections (`select: true` or `select: false`) within maps at path "' + path + '.' + subpath + '"');
}
});
}
if (utils.hasUserDefinedProperty(obj, 'ref')) {
_mapType.ref = obj.ref;
}
}
this.$__schemaType = schema.interpretAsType(mapPath, _mapType, options);
};
module.exports = SchemaMap;
View on GitHub (pinned to 49cdab0136)
Solutions
- Remove `select` from the map's value schema paths; hide those fields at query time with an explicit projection on the parent (`.select('-data')` or field filtering in the serializer layer)
- Clone the value schema and strip select options before passing it as `of`: `const s = base.clone(); s.eachPath((p, t) => delete t.options.select)`
- Restructure to a plain subdocument array path (`items: [base]`) where select is honored, or keep secrets in a separate model
- Enforce a lint/review rule: no select options inside Map-of-subdocument definitions
Example fix
// before
const secretSchema = new Schema({ token: { type: String, select: false } });
new Schema({ apiKeys: { type: Map, of: secretSchema } });
// after
const secretSchema = new Schema({ token: String });
new Schema({ apiKeys: { type: Map, of: secretSchema } });
// and filter token out in the response layer (toJSON transform or serializer) Defensive patterns
Strategy: validation
Validate before calling
function stripSelectOptions(subSchema) {
subSchema.eachPath((p, type) => { delete type.options.select; });
return subSchema;
}
new Schema({ data: { type: Map, of: stripSelectOptions(baseSchema.clone()) } }); Try / catch
try { new Schema({ data: { type: Map, of: valueSchema } }); } catch (err) { if (/schema-level projections/.test(err.message)) { stripSelectOptions(valueSchema); } else throw err; } Prevention
- Keep select projections on top-level paths only
- Filter secret fields in the serializer/toJSON layer instead of Map subschemas
- Lint for select options inside Map `of` definitions
When it happens
Trigger: `new Schema({ data: { type: Map, of: new Schema({ secret: { type: String, select: false } }) } })` — any Map whose `of` resolves to a schema containing a path with select true/false. Note the check runs on the *value* schema's paths via eachPath, so even nested subdocument paths with select trigger it.
Common situations: Reusing an existing subdocument schema (with hidden fields like password hashes or tokens) as the `of` type of a Map; adding `select: false` for security without noticing the schema is embedded in a Map; schema libraries sharing a base sub-schema across Map and non-Map parents.
Related errors
- Invalid select() argument. Must be string or object.
- Aggregate `near()` must be called with non-nullish argument
- Invalid sort() argument. Must be a string or object.
- If thenExpr or elseExpr is string, it must be either $$DESCE
- Field `${path}` is not in schema and strict mode is set to t
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/7999b6164f055940.
Report an issue: GitHub.