mongodb/laravel-mongodb · error · InvalidArgumentException

Cannot have both "id" and "_id" fields.

Error message

Cannot have both "id" and "_id" fields.

What it means

When preparing an attribute array for a MongoDB query, the library renames the Eloquent-style "id" field to MongoDB's "_id". If both "id" and "_id" are present in the same array with different values, the rename would silently lose one value, so an InvalidArgumentException is thrown.

Solutions

  1. Remove one of the two keys from the payload array before passing it to the query
  2. Normalize your data to a single convention (Eloquent 'id' or MongoDB '_id') before building the payload
  3. If both keys must exist and be equal, keep only '_id' (the library would map 'id' to it anyway)
  4. Audit array_merge/array_replace call sites that combine model attributes with raw documents

Example fix

// before
$data = ['id' => '64b0...', '_id' => 'different'];
$collection->insertOne($data);
// after
unset($data['id']); // keep only '_id'
$collection->insertOne($data);
Defensive patterns

Strategy: validation

Validate before calling

if (array_key_exists('id', $data) && array_key_exists('_id', $data) && $data['id'] !== $data['_id']) {
    throw new InvalidArgumentException('Payload must not define both "id" and "_id".');
}

Type guard

function hasIdConflict(array $values): bool {
    return array_key_exists('id', $values) && array_key_exists('_id', $values) && $values['id'] !== $values['_id'];
}

Try / catch

try {
    $model->create($data);
} catch (\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), '"id" and "_id"')) {
        unset($data['id']);
        $model->create($data);
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Calling any query/write method (e.g. insert, create, update, where-style payload paths) via prepareFieldsForQuery with an array containing keys 'id' and '_id' whose values differ, at the document root (or in an embedded document when renameEmbeddedIdField is enabled).

Common situations: Merging Eloquent model attributes (which use 'id') with raw MongoDB documents (which use '_id'); building payloads by array_merge where a raw BSON doc and a hydrated model both contribute; deserializing JSON that includes both id keys.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15). Data as JSON: /api/errors/60d99e61ee5afccb. Report an issue: GitHub.

Appendix: source

Thrown at src/Query/Grammar.php:50

{
    /**
     * Prepare fields for the MongoDB query by aliasing "id" to "_id" and handling arrow notation.
     * Users can override this method to customize field aliasing behavior.
     *
     * @param array<string, mixed> $values The values to prepare
     * @param bool                 $root   Whether this is the root level (affects embedded id field handling)
     * @psalm-param T $values
     *
     * @return array<string, mixed> The prepared values
     * @psalm-return T
     *
     * @template T of array
     */
    public function prepareFieldsForQuery(array $values, bool $root = true): array
    {
        if (array_key_exists('id', $values) && ($root || $this->connection->getRenameEmbeddedIdField())) {
            if (array_key_exists('_id', $values) && $values['id'] !== $values['_id']) {
                throw new InvalidArgumentException('Cannot have both "id" and "_id" fields.');
            }

            $values['_id'] = $values['id'];
            unset($values['id']);
        }

        foreach ($values as $key => $value) {
            if (! is_string($key)) {
                continue;
            }

            // "->" arrow notation for subfields is an alias for "." dot notation
            if (str_contains($key, '->')) {
                $newKey = str_replace('->', '.', $key);
                if (array_key_exists($newKey, $values) && $value !== $values[$newKey]) {
                    throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newKey));
                }

View on GitHub (pinned to 0634653039)