laravel/framework · error · InvalidArgumentException

Non-associative array passed to decrementEach method.

Error message

Non-associative array passed to decrementEach method.

What it means

Thrown by decrementEach when a key in $columns is not a string (a sequential/list array rather than associative). decrementEach needs column-name => amount pairs; a list like [5, 10] has no column targets. The is_numeric check fires first, so a numeric-only list triggers the prior numeric error.

Source

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

        return $this->decrementEach([$column => $amount], $extra);
    }

    /**
     * Decrement the given column's values by the given amounts.
     *
     * @param  array<string, float|int|numeric-string>  $columns
     * @param  array<string, mixed>  $extra
     * @return int<0, max>
     *
     * @throws \InvalidArgumentException
     */
    public function decrementEach(array $columns, array $extra = [])
    {
        foreach ($columns as $column => $amount) {
            if (! is_numeric($amount)) {
                throw new InvalidArgumentException("Non-numeric value passed as decrement amount for column: '$column'.");
            } elseif (! is_string($column)) {
                throw new InvalidArgumentException('Non-associative array passed to decrementEach method.');
            }

            $columns[$column] = $this->raw("{$this->grammar->wrap($column)} - $amount");
        }

        return $this->update(array_merge($columns, $extra));
    }

    /**
     * Delete records from the database.
     *
     * @param  mixed  $id
     * @return int
     */
    public function delete($id = null)
    {
        // If an ID is passed to the method, we will set the where clause to check the
        // ID to let developers to simply and quickly remove a single row from this

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Pass an associative array: `['stock' => 1, 'reserved' => 1]`.
  2. Preserve keys: use `collect(...)->mapWithKeys(...)` or `array_combine`.
  3. Decode JSON as object when the source is a key map.
  4. Assert associativity before calling.

Example fix

// before
$amounts = array_filter($request->all(), 'is_numeric');
$model->decrementEach(array_values($amounts));
// => Non-associative array passed to decrementEach method.

// after
$pairs = ['stock' => $request->integer('stock', 0)];
$model->decrementEach($pairs);
Defensive patterns

Strategy: type-guard

Validate before calling

if (array_keys($columns) === range(0, count($columns) - 1)) {
    throw new \InvalidArgumentException('decrementEach expects an associative [column => amount] map.');
}

Type guard

/** @param array<string, numeric> $a */
function isAssociativeMap(array $a): bool
{
    return $a !== [] && array_keys($a) !== range(0, count($a) - 1);
}

Try / catch

// Shape/type bug: validate the map before calling rather than catch.

Prevention

When it happens

Trigger: `decrementEach([5, 10])`. Passing `array_values($assoc)`. Building the map with array functions that reindex numerically.

Common situations: Transforming an associative map through array_map/array_filter that loses keys; merging with a list; JSON decoded as a list rather than an object.

Related errors


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