mongodb/laravel-mongodb · error · InvalidArgumentException

Cannot sort by '_score' in ascending order; Atlas Search…

Error message

Cannot sort by '_score' in ascending order; Atlas Search relevance score always sorts descending.

What it means

Atlas Search relevance scores are always ranked descending (highest score first). The Scout engine rejects an explicit ascending '_score' sort with an InvalidArgumentException, validated before any I/O.

Solutions

  1. Remove the '_score' order or force its direction to 'desc'
  2. For lowest-relevance-first results, sort desc by _score and reverse the page client-side (or use $searchMeta differently)
  3. Filter user-supplied sort directions so '_score' is always mapped to 'desc'
  4. Catch InvalidArgumentException and fall back to relevance-descending order

Example fix

// before
$builder->orderBy('_score', $dir); // $dir may be 'asc'
// after
$builder->orderBy('_score', 'desc'); // relevance is always descending
Defensive patterns

Strategy: validation

Validate before calling

foreach ($builder->orders as $o) {
    if ($o['column'] === '_score' && $o['direction'] === 'asc') {
        throw new InvalidArgumentException("'_score' must sort descending.");
    }
}

Try / catch

try {
    $builder->orderBy('_score', $dir);
} catch (\InvalidArgumentException $e) {
    $builder->orderBy('_score', 'desc'); // coerce relevance to descending
}

Prevention

When it happens

Trigger: Calling search()/paginate() with ->orderBy('_score', 'asc') on the Scout builder; any code path that adds an ascending order whose column is '_score'.

Common situations: Building dynamic sort UIs where the user can pick asc/desc without excluding the relevance column; defaulting all sorts to asc.

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/9eb987d74e4852cc. Report an issue: GitHub.

Appendix: source

Thrown at src/Scout/ScoutEngine.php:209

        $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))));
            $cursor->setTypeMap(self::TYPEMAP);

            return $cursor->toArray();
        }

View on GitHub (pinned to 0634653039)