laravel/framework · error · UnexpectedValueException

::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(callable $callback, ...$initial) when the reducer callable returns a non-array value on any iteration. reduceSpread expects each step to return an array so it can spread the result as arguments into the next call (list-destructuring pattern); a non-array return breaks that contract and throws UnexpectedValueException with the class and actual type.

Solutions

  1. Ensure the reducer always returns an array on every code path (including the final iteration).
  2. Use plain reduce() if you only need a single accumulated value.
  3. Add a return-type check / unit test asserting array output for each branch.

Example fix

// before
$result = $collection->reduceSpread(function ($carry, $item) {
    if ($item === null) return null;
    return [$carry[0] + $item];
}, 0);
// after
$result = $collection->reduceSpread(function ($carry, $item) {
    if ($item === null) return $carry;
    return [$carry[0] + $item];
}, 0);
Defensive patterns

Strategy: validation

Validate before calling

// Verify the reducer returns array on a sample before relying on it
$test = $callback(...array_merge($initial, [$collection->first(), $collection->keys()->first()]));
if (! is_array($test)) {
    throw new \UnexpectedValueException('reducer must return array');
}

Type guard

// Ensure every branch returns an array
$callback = function (array $carry, $item): array {
    // ...all paths return array
    return $carry;
};

Try / catch

try {
    $result = $collection->reduceSpread($callback, ...$initial);
} catch (\UnexpectedValueException $e) {
    // fall back to plain reduce
    $result = $collection->reduce($callback, $initial);
}

Prevention

When it happens

Trigger: Passing a reducer that returns a scalar/object/null on some iteration; forgetting to return the accumulator array; early-return inside the callback that yields null.

Common situations: Refactoring a reduce into reduceSpread for the spread-arg ergonomics; callback that conditionally returns and misses a branch; third-party reducer reused without adapting its return shape.

Related errors


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

Appendix: 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 e0f6eb3518)