Automattic/mongoose · error · TypeError
$near must be either an array or an object with a $geometry
Error message
$near must be either an array or an object with a $geometry property
What it means
cast$near (used by $near and $nearSphere on geospatial paths) accepts three shapes: a legacy coordinate array ([x, y]), an object with a $geometry key (GeoJSON, optionally with $maxDistance/$minDistance), or an array value handled by the array caster. Anything else — a string, a plain { lat, lng } object without $geometry, null — falls through to this TypeError.
Source
Thrown at lib/schema/operators/geospatial.js:33
exports.cast$near = cast$near;
exports.cast$within = cast$within;
function cast$near(val) {
const SchemaArray = require('../array');
if (Array.isArray(val)) {
castArraysOfNumbers(val, this);
return val;
}
_castMinMaxDistance(this, val);
if (val?.$geometry) {
return cast$geometry(val, this);
}
if (!Array.isArray(val)) {
throw new TypeError('$near must be either an array or an object ' +
'with a $geometry property');
}
return SchemaArray.prototype.castForQuery.call(this, null, val);
}
function cast$geometry(val, self) {
switch (val.$geometry.type) {
case 'Polygon':
case 'LineString':
case 'Point':
castArraysOfNumbers(val.$geometry.coordinates, self);
break;
default:
// ignore unknowns
break;
}
View on GitHub (pinned to 49cdab0136)
Solutions
- Use legacy pairs: `{ loc: { $near: [lng, lat] } }` — note longitude first
- Use proper GeoJSON: `{ loc: { $near: { $geometry: { type: 'Point', coordinates: [lng, lat] }, $maxDistance: 5000 } } }`
- Normalize client coordinates (lat/lng objects or strings) into one of the two accepted shapes in your query-building layer
- For $geoNear aggregation, use its near/distanceField options instead
Example fix
// before
Model.find({ loc: { $near: { lat: 34.05, lng: -118.24 } } });
// after
Model.find({ loc: { $near: { $geometry: { type: 'Point', coordinates: [-118.24, 34.05] }, $maxDistance: 5000 } } }); Defensive patterns
Strategy: validation
Validate before calling
function toNearFilter(coords /* { lng, lat } or [lng, lat] */, maxDistance) {
const point = Array.isArray(coords) ? coords : [coords.lng, coords.lat];
if (typeof point[0] !== 'number' || typeof point[1] !== 'number') {
throw new Error('$near needs [lng, lat] numbers or { $geometry }');
}
return { $near: { $geometry: { type: 'Point', coordinates: point }, $maxDistance: maxDistance } };
} Type guard
function isValidNear(v) {
return Array.isArray(v) || (v != null && typeof v === 'object' && v.$geometry != null);
} Try / catch
try { await Model.find({ loc: { $near: value } }); } catch (err) { if (/\$near must be either an array/.test(err.message)) { return badRequest('$near requires [lng, lat] or { $geometry: ... }'); } throw err; } Prevention
- Normalize lat/lng client payloads into [lng, lat] at the boundary
- Always wrap GeoJSON in $geometry for $near
- Remember coordinate order is longitude, latitude
When it happens
Trigger: `Model.find({ loc: { $near: '12,34' } })` (string), `{ loc: { $near: { lat: 12, lng: 34 } } }` (missing $geometry wrapper), `{ loc: { $near: {} } }`. Note: an object with only $maxDistance/$minDistance but no $geometry also reaches the throw.
Common situations: Sending Google-Maps-style { lat, lng } objects instead of GeoJSON; forgetting the $geometry wrapper around GeoJSON; confusing $near query syntax with the $geoNear aggregation stage; clients sending 'lat,lng' strings from URL query params.
Related errors
- Invalid geoJSON type for $geoWithin "${geoWithinType}", must
- Bad $within parameter: ${JSON.stringify(val)}
- invalid argument
- Invalid $within $box argument. Expected an array, received $
- Invalid addFields() argument. Must be an object
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/da9579d93b9e6097.
Report an issue: GitHub.