laravel/framework · error · InvalidArgumentException

Nested arrays may not be passed to whereIn method.

Error message

Nested arrays may not be passed to whereIn method.

What it means

whereIn() stores its values array as bindings, and the grammar flattens exactly one level. After pushing the values into $this->wheres, the builder verifies count($values) === count(Arr::flatten($values, 1)); a mismatch means a nested array was passed, which would silently corrupt bindings or emit wrong SQL, so it throws InvalidArgumentException.

Solutions

  1. Flatten or map your input to a flat list of scalars before passing: ->whereIn('id', $rows->pluck('id')->all()).
  2. For multi-column tuple matching use whereIn with an array of arrays via whereIn('col', $query) subquery form, or use whereRowValues per row.
  3. Switch to whereJsonContains if you actually need array-membership semantics on a JSON column.

Example fix

// before
->whereIn('user_id', $orders->map(fn ($o) => $o->user_ids))  // user_ids is an array

// after
->whereIn('user_id', $orders->flatMap(fn ($o) => $o->user_ids))
Defensive patterns

Strategy: validation

Validate before calling

if (Arr::isList($values) && count($values) === count(Arr::flatten($values, 1))) {
    // safe: flat list of scalars
}
$values = array_values(Arr::flatten($values, 1)); // force-flatten to a scalar list
$query->whereIn('id', $values);

Type guard

function isFlatScalarList(array $values): bool
{
    if (count($values) !== count(\Illuminate\Support\Arr::flatten($values, 1))) {
        return false;
    }
    foreach ($values as $v) {
        if (is_array($v) || is_object($v) && ! ($v instanceof \Illuminate\Contracts\Database\Query\Expression)) {
            return false;
        }
    }
    return true;
}

Prevention

When it happens

Trigger: ->whereIn('id', [[1, 2], [3, 4]]), ->whereIn('role', $collectionOfArrays), or any case where an element of $values is itself an array (after Arrayable casting).

Common situations: Pluck()ing two columns and forgetting to extract one ('->pluck(['a','b'])'); groupBy + pluck returning nested structure; passing an Eloquent collection of models instead of an array of scalars; multi-column tuples mistakenly passed to whereIn.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/8dc54a35e6cc07ac. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Database/Query/Builder.php:1447

        if ($this->isQueryable($values)) {
            [$query, $bindings] = $this->createSub($values);

            $values = [new Expression($query)];

            $this->addBinding($bindings, 'where');
        }

        // Next, if the value is Arrayable we need to cast it to its raw array form so we
        // have the underlying array value instead of an Arrayable object which is not
        // able to be added as a binding, etc. We will then add to the wheres array.
        if ($values instanceof Arrayable) {
            $values = $values->toArray();
        }

        $this->wheres[] = ['type' => $type, 'column' => $column, 'values' => $values, 'boolean' => $boolean];

        if (count($values) !== count(Arr::flatten($values, 1))) {
            throw new InvalidArgumentException('Nested arrays may not be passed to whereIn method.');
        }

        // Finally, we'll add a binding for each value unless that value is an expression
        // in which case we will just skip over it since it will be the query as a raw
        // string and not as a parameterized place-holder to be replaced by the PDO.
        $this->addBinding($this->cleanBindings($values), 'where');

        return $this;
    }

    /**
     * Add an "or where in" clause to the query.
     *
     * @param  \Illuminate\Contracts\Database\Query\Expression|string  $column
     * @param  mixed  $values
     * @return $this
     */
    public function orWhereIn($column, $values)

View on GitHub (pinned to e0f6eb3518)