mongodb/laravel-mongodb · error · InvalidArgumentException

Cannot have both " " and " " fields.

Error message

Cannot have both "%s" and "%s" fields.

What it means

The grammar rewrites the '->' arrow notation subfield alias into '.' dot notation. If after the rewrite the target dotted key already exists in the same array with a different value, both entries would conflict, so an InvalidArgumentException naming both keys is thrown.

Solutions

  1. Use one notation consistently: pick 'address.city' (dot) or 'address->city' (arrow), not both
  2. Remove the duplicate key whose value is wrong before building the payload
  3. Make both key values identical if the duplication is intentional (the alias is then applied without error)
  4. Add a lint rule or helper that normalizes '->' to '.' in payload keys

Example fix

// before
$data = ['address->city' => 'Lyon', 'address.city' => 'Paris'];
Model::create($data);
// after
$data = ['address.city' => 'Paris'];
Model::create($data);
Defensive patterns

Strategy: validation

Validate before calling

foreach ($data as $k => $v) {
    if (str_contains($k, '->')) {
        $dot = str_replace('->', '.', $k);
        if (array_key_exists($dot, $data) && $v !== $data[$dot]) {
            throw new InvalidArgumentException("Conflicting keys '$k' and '$dot'");
        }
    }
}

Type guard

function hasArrowDotConflict(array $values): bool {
    foreach ($values as $k => $v) {
        if (is_string($k) && str_contains($k, '->')) {
            $dot = str_replace('->', '.', $k);
            if (array_key_exists($dot, $values) && $v !== $values[$dot]) return true;
        }
    }
    return false;
}

Try / catch

try {
    $model->create($data);
} catch (\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'Cannot have both')) {
        $data = normalizeArrowKeys($data); // replace '->' with '.'
        $model->create($data);
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Passing a payload to prepareFieldsForQuery (via insert/create/update attributes) containing e.g. both 'address->city' and 'address.city' keys with different values.

Common situations: Mixing Laravel dot/arrow syntax in nested attribute arrays; copying field definitions from two code styles; template-built payloads where some keys use '->' and others use '.'.

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/915bc82b06b7b725. Report an issue: GitHub.

Appendix: source

Thrown at src/Query/Grammar.php:66

        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));
                }

                $values[$newKey] = $value;
                unset($values[$key]);
                $key = $newKey;
            }

            // ".id" subfield are alias for "._id"
            if (str_ends_with($key, '.id') && $this->connection->getRenameEmbeddedIdField()) {
                $newKey = substr($key, 0, -3) . '._id';
                if (array_key_exists($newKey, $values) && $value !== $values[$newKey]) {
                    throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newKey));
                }

                $values[$newKey] = $value;
                unset($values[$key]);
            }
        }

View on GitHub (pinned to 0634653039)