mongodb/laravel-mongodb · error · InvalidArgumentException

Cannot sort by a field named 'score' together with Atlas…

Error message

Cannot sort by a field named 'score' together with Atlas Search's '_score' relevance sort.

What it means

The Atlas Search Scout engine maps the special '_score' order to relevance ranking. If the query also sorts by a user field literally named 'score', the two orders are ambiguous/conflicting, so an InvalidArgumentException is thrown before any I/O.

Solutions

  1. Rename the model field from 'score' to something like 'user_score' in documents and mappings
  2. Drop the explicit orderBy('score') when ordering by '_score' relevance
  3. Wrap the field, e.g. sort on 'stats.score' instead of 'score', to avoid the reserved-name clash
  4. Catch InvalidArgumentException in the search path to surface a clear user-facing message

Example fix

// before
$builder->orderBy('_score')->orderBy('score');
// after
$builder->orderBy('_score')->orderBy('user_score'); // field renamed in documents/mappings
Defensive patterns

Strategy: validation

Validate before calling

$cols = array_column($builder->orders, 'column');
if (in_array('_score', $cols, true) && in_array('score', $cols, true)) {
    throw new InvalidArgumentException("Cannot combine 'score' and '_score' sorts.");
}

Try / catch

try {
    $results = $model->search($q)->orderBy('_score')->orderBy('score')->get();
} catch (\InvalidArgumentException $e) {
    $results = $model->search($q)->orderBy('_score')->get(); // drop the conflicting sort
}

Prevention

When it happens

Trigger: Calling ScoutEngine::search()/paginate() with a Laravel Scout Builder whose orders contain both column '_score' and column 'score' (e.g. ->orderBy('_score')->orderBy('score')).

Common situations: Models that legitimately have a 'score' field being searched with Atlas Search relevance sort; combining app-level default ordering with a relevance sort added in the search flow.

Related errors


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

Appendix: source

Thrown at src/Scout/ScoutEngine.php:204

    {
        assert(is_int($perPage), new TypeError(sprintf('Argument #2 ($perPage) must be of type int, %s given', get_debug_type($perPage))));
        assert(is_int($page), new TypeError(sprintf('Argument #3 ($page) must be of type int, %s given', get_debug_type($page))));

        $builder = clone $builder;
        $builder->take($perPage);

        return $this->performSearch($builder, $perPage * ($page - 1));
    }

    /**
     * Perform the given search on the engine.
     */
    private function performSearch(Builder $builder, ?int $offset = null): array
    {
        // Validate sort before any I/O so unit tests can assert on these exceptions without a real collection.
        $columns = array_column($builder->orders, 'column');
        if (in_array('_score', $columns, true) && in_array('score', $columns, true)) {
            throw new InvalidArgumentException("Cannot sort by a field named 'score' together with Atlas Search's '_score' relevance sort.");
        }

        foreach ($builder->orders as $order) {
            if ($order['column'] === '_score' && $order['direction'] === 'asc') {
                throw new InvalidArgumentException("Cannot sort by '_score' in ascending order; Atlas Search relevance score always sorts descending.");
            }
        }

        $collection = $this->getSearchableCollection($builder->model);

        if ($builder->callback) {
            $cursor = call_user_func(
                $builder->callback,
                $collection,
                $builder->query,
                $offset,
            );
            assert($cursor instanceof CursorInterface, new LogicException(sprintf('The search builder closure must return a MongoDB cursor, %s returned', get_debug_type($cursor))));

View on GitHub (pinned to 0634653039)