laravel/framework · error · InvalidArgumentException

The number of columns must match the number of values

Error message

The number of columns must match the number of values

What it means

Thrown by whereRowValues when count($columns) !== count($values). whereRowValues builds a row-value constructor comparison `(col1, col2) = (v1, v2)` which SQL requires to have equal arity on both sides. Mismatched lengths would generate invalid SQL or silently misalign bindings.

Source

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

        return $this;
    }

    /**
     * Adds a where condition using row values.
     *
     * @param  array  $columns
     * @param  string  $operator
     * @param  array  $values
     * @param  string  $boolean
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function whereRowValues($columns, $operator, $values, $boolean = 'and')
    {
        if (count($columns) !== count($values)) {
            throw new InvalidArgumentException('The number of columns must match the number of values');
        }

        $type = 'RowValues';

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

        $this->addBinding($this->cleanBindings($values));

        return $this;
    }

    /**
     * Adds an or where condition using row values.
     *
     * @param  array  $columns
     * @param  string  $operator
     * @param  array  $values
     * @return $this

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Align the arrays: ensure count($columns) === count($values) by adding the missing column or removing the extra value.
  2. Build both sides from the same source: `whereRowValues(array_keys($row), '=', array_values($row))`.
  3. Add an assertion/guard before calling: `if (count($cols) !== count($vals)) throw ...`.
  4. Prefer multiple where clauses if you do not actually need row-tuple semantics.

Example fix

// before
$query->whereRowValues(['tenant_id','user_id'], '=', [$tenantId])->get();
// => The number of columns must match the number of values

// after
$query->whereRowValues(['tenant_id','user_id'], '=', [$tenantId, $userId])->get();
Defensive patterns

Strategy: validation

Validate before calling

if (count($columns) !== count($values)) {
    throw new \InvalidArgumentException('whereRowValues: columns ('.count($columns).') vs values ('.count($values).') mismatch.');
}

Type guard

/** @param list<mixed> $columns @param list<mixed> $values */
function sameArity(array $columns, array $values): bool
{
    return count($columns) === count($values);
}

Try / catch

// Validate arity before the call; catching here is rarely useful because the
// mismatch is a programming bug, not transient state.

Prevention

When it happens

Trigger: `whereRowValues(['a','b'], '=', [1])`. Building composite keys where one side is built dynamically and the other statically. Selecting 3 columns but only providing 2 values from a composite unique key lookup.

Common situations: Composite primary key lookups with `(c1,c2,c3) = (?,?)`; polymorphic row comparisons where one tuple is computed and the other hand-coded; refactoring that adds a column on one side but not the other.

Related errors


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