laravel/framework · error · UnexpectedValueException

%s::reduceSpread expects reducer to return an array, but got

Error message

%s::reduceSpread expects reducer to return an array, but got a '%s' instead.

What it means

Thrown by reduceSpread() when the reducer callback returns a non-array value on any iteration. reduceSpread spreads the accumulator array into the next callback call (like JS array destructuring), so the callback must always return an array. UnexpectedValueException reports the actual returned type.

Source

Thrown at src/Illuminate/Collections/Traits/EnumeratesValues.php:889

    /**
     * Reduce the collection to multiple aggregate values.
     *
     * @param  callable  $callback
     * @param  mixed  ...$initial
     * @return array
     *
     * @throws \UnexpectedValueException
     */
    public function reduceSpread(callable $callback, ...$initial)
    {
        $result = $initial;

        foreach ($this as $key => $value) {
            $result = call_user_func_array($callback, array_merge($result, [$value, $key]));

            if (! is_array($result)) {
                throw new UnexpectedValueException(sprintf(
                    "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.",
                    class_basename(static::class), gettype($result)
                ));
            }
        }

        return $result;
    }

    /**
     * Reduce an associative collection to a single value.
     *
     * @template TReduceWithKeysInitial
     * @template TReduceWithKeysReturnType
     *
     * @param  callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType  $callback
     * @param  TReduceWithKeysInitial  $initial
     * @return TReduceWithKeysInitial|TReduceWithKeysReturnType

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Ensure the callback always returns an array; add a return statement on every branch.
  2. If you want a scalar accumulator, use reduce() instead of reduceSpread().
  3. Add a unit test asserting the callback's return type.

Example fix

// before
[$sum, $count] = $collection->reduceSpread(
    fn ($sum, $count, $item) => $sum + $item->price
);

// after
[$sum, $count] = $collection->reduceSpread(
    fn ($sum, $count, $item) => [$sum + $item->price, $count + 1]
);
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the reducer returns array before reduceSpread
$probe = $callback(...[...$initial, $collection->first(), 0]);
if (! is_array($probe)) {
    throw new \LogicException('reducer must return an array');
}

Type guard

function returnsArray(callable $cb, array $args): bool {
    return is_array($cb(...$args));
}

Try / catch

try {
    [$a, $b] = $collection->reduceSpread($cb);
} catch (\UnexpectedValueException $e) {
    // fix the callback to always return an array; reduceSpread can't recover
    throw $e;
}

Prevention

When it happens

Trigger: Calling $collection->reduceSpread(fn ($sum, $count, $item) => $sum + $item) where the callback returns a scalar. Forgetting to return an array, or an early return/break path returning null.

Common situations: Misunderstanding reduceSpread semantics (treating it like reduce). Refactoring from a scalar reduce() without updating the callback to return an array. A branch in the callback that forgets to return.

Related errors


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