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|TReduceWithKeysReturnTypeView on GitHub (pinned to bd6b5437e6)
Solutions
- Ensure the callback always returns an array; add a return statement on every branch.
- If you want a scalar accumulator, use reduce() instead of reduceSpread().
- 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
- Use reduce() when you need a scalar accumulator; reserve reduceSpread() for tuple/array accumulators.
- Always return an array on every branch of the reducer callback.
- Unit-test the reducer in isolation to confirm it returns an array.
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
- Collection should only include [%s] items, but '%s' found at
- Array value for key [%s] must be an array, %s found.
- Array value for key [%s] must be a boolean, %s found.
- Array value for key [%s] must be a float, %s found.
- Items cannot be represented by a scalar value.
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/4bfdd2a046c2ea37.json.
Report an issue: GitHub.