mongodb/laravel-mongodb · error · InvalidArgumentException
The value used as a document id or relation key cannot…
Error message
The value used as a document id or relation key cannot contain the MongoDB operator "%s".
What it means
The library scans document ids / relation keys and rejects values that look like MongoDB query operators — array keys starting with '$' such as '$gt' or '$ne'. This prevents untrusted input from injecting operator documents where a plain scalar id is expected, e.g. when finding by _id or resolving relations.
Solutions
- Cast ids to string/int before use: ->find((string) $request->input('id')).
- Validate input is scalar before treating it as a key: if (!is_scalar($id)) abort(400).
- Use the library's ObjectId conversion helpers instead of raw arrays for ids.
- Sanitize nested user input so no array keys beginning with '$' survive (recursive whitelist).
Example fix
// before
$user = User::find($request->input('id')); // id = {"$gt": ""}
// after
$id = (string) $request->input('id');
$user = User::find($id); Defensive patterns
Strategy: type-guard
Validate before calling
function assertScalarId(mixed $id): string|int {
if (!is_string($id) && !is_int($id)) {
throw new InvalidArgumentException('Document id must be scalar');
}
return $id;
} Type guard
function isSafeId(mixed $v): bool {
return is_string($v) || is_int($v);
} Prevention
- Cast ids to string/int at the request boundary.
- Reject nested arrays in ids and relation keys.
- Never pass raw JSON payloads as ids.
When it happens
Trigger: User-controlled input like ['id' => ['$gt' => '']] reaching ->find($id), ->findOrFail($id), relation resolution (belongsTo/hasMany keys), or ->convertKey(); any array value passed as a key with a '$'-prefixed string key at any nesting depth.
Common situations: Passing raw request input directly to find()/where on _id; noSQL-injection attempts via JSON payloads; storing operator-shaped arrays in document id columns.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Between $values must be a list with exactly two elements…
- 2nd argument of () must be "null" when 1st argument is an…
- Distinct queries cannot be used for pagination. Use GroupBy…
- Too few arguments to function
- First argument of must be a field path as "string". Got
AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15).
Data as JSON: /api/errors/1593ea917985c4f3.
Report an issue: GitHub.
Appendix: source
Thrown at src/Query/Builder.php:1260
return $id;
}
/**
* A plain array without "$"-prefixed keys is allowed, so composite _id values keep working.
*
* @internal
*
* @throws InvalidArgumentException when the value contains a MongoDB operator.
*/
public static function assertKeyIsNotOperator(mixed $value): void
{
if (! is_array($value)) {
return;
}
foreach ($value as $key => $item) {
if (is_string($key) && str_starts_with($key, '$')) {
throw new InvalidArgumentException(sprintf(
'The value used as a document id or relation key cannot contain the MongoDB operator "%s".',
$key,
));
}
self::assertKeyIsNotOperator($item);
}
}
/**
* Add a basic where clause to the query.
*
* If 1 argument, the signature is: where(array|Closure $where)
* If 2 arguments, the signature is: where(string $column, mixed $value)
* If 3 arguments, the signature is: where(string $colum, string $operator, mixed $value)
*
* With 1 or 2 arguments, an array value is read as a MongoDB operator document,
* so never pass unvalidated input there, it is open to MQL injection.View on GitHub (pinned to 0634653039)