laravel/framework · error · MultipleRecordsFoundException

%s records were found.

Error message

%s records were found.

What it means

Relation::sole() (and Builder::sole()) fetch up to 2 rows and require exactly one. If more than one row matches, MultipleRecordsFoundException (RuntimeException) is thrown with the count, because 'sole' asserts the caller's expectation of uniqueness. It is distinct from ModelNotFoundException (zero rows).

Source

Thrown at src/Illuminate/Database/Eloquent/Relations/Relation.php:271

     *
     * @param  array|string  $columns
     * @return TRelatedModel
     *
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<TRelatedModel>
     * @throws \Illuminate\Database\MultipleRecordsFoundException
     */
    public function sole($columns = ['*'])
    {
        $result = $this->limit(2)->get($columns);

        $count = $result->count();

        if ($count === 0) {
            throw (new ModelNotFoundException)->setModel(get_class($this->related));
        }

        if ($count > 1) {
            throw new MultipleRecordsFoundException($count);
        }

        return $result->first();
    }

    /**
     * Execute the query as a "select" statement.
     *
     * @param  array  $columns
     * @return \Illuminate\Database\Eloquent\Collection<int, TRelatedModel>
     */
    public function get($columns = ['*'])
    {
        return $this->query->get($columns);
    }

    /**
     * Touch all of the related models for the relationship.

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Use first() or firstOrFail() if more than one row may legitimately match.
  2. Add a unique constraint/index at the DB level to enforce the uniqueness sole() assumes.
  3. Tighten the where clause to guarantee a single row (e.g. add scope/order+limit 1 explicitly).
  4. Catch MultipleRecordsFoundException to handle the ambiguity explicitly.

Example fix

// before
$post = User::where('slug', $slug)->sole(); // throws if duplicate slugs

// after
$post = User::where('slug', $slug)->firstOrFail();
// or enforce uniqueness:
// schema: $table->string('slug')->unique();
Defensive patterns

Strategy: try-catch

Validate before calling

if ($query->count() > 1) {
    throw new \Illuminate\Database\MultipleRecordsFoundException($query->count());
}
$query->sole();

Type guard

function isUniqueMatch(\Illuminate\Database\Eloquent\Builder $query): bool {
    return $query->clone()->limit(2)->count() === 1;
}

Try / catch

try {
    return $query->sole();
} catch (\Illuminate\Database\MultipleRecordsFoundException $e) {
    // handle ambiguity: pick one, log, or fail
    return $query->first();
}

Prevention

When it happens

Trigger: Calling ->sole() on a relation or query that matches 2+ rows, e.g. User::where('email', $e)->sole() when duplicates exist, or $user->latestPost()->sole() when there are multiple.

Common situations: Data with unintended duplicates (missing unique index); querying by a non-unique column; race conditions inserting duplicates before a unique constraint; using sole() where first() was intended.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/1d03ca45add880c9.json. Report an issue: GitHub.