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

  1. Cast ids to string/int before use: ->find((string) $request->input('id')).
  2. Validate input is scalar before treating it as a key: if (!is_scalar($id)) abort(400).
  3. Use the library's ObjectId conversion helpers instead of raw arrays for ids.
  4. 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

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


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)