laravel/framework · error · InvalidArgumentException

Generators should not be passed directly to LazyCollection…

Error message

Generators should not be passed directly to LazyCollection. Instead, pass a generator function.

What it means

Thrown by the LazyCollection constructor when a Generator instance is passed directly as the source. A Generator can only be iterated once and is consumed during operations, which breaks LazyCollection's laziness and re-iteration guarantees. The library requires a Closure that *produces* a Generator (a factory) so it can be re-created on each traversal.

Solutions

  1. Pass a closure that returns the generator: new LazyCollection(fn () => myGeneratorFunc()).
  2. If the source is already iterable, pass it directly (array, Arrayable) instead of a generator.
  3. For a generator held in a variable, wrap its producer: new LazyCollection(function () use (...) { yield ...; }).

Example fix

// before
$lazy = new LazyCollection(readLines($file));
// after
$lazy = new LazyCollection(fn () => readLines($file));
Defensive patterns

Strategy: type-guard

Validate before calling

if ($source instanceof \Generator) {
    throw new \InvalidArgumentException('Wrap the generator in a closure.');
}

Type guard

function lazySource($source) {
    if ($source instanceof \Generator) {
        return fn () => $source; // not ideal; better to capture the factory
    }
    return $source;
}
// Preferred: always pass the generator-producing closure, not the generator.

Try / catch

try {
    $lazy = new \Illuminate\Support\LazyCollection($source);
} catch (\InvalidArgumentException $e) {
    $lazy = new \Illuminate\Support\LazyCollection(fn () => yield from $source);
}

Prevention

When it happens

Trigger: new LazyCollection($generator) where $generator is the return value of a generator function (e.g. new LazyCollection(myGeneratorFunc())). Also when wrapping a yield-based iterator from another library directly.

Common situations: Reading a file lazily via a generator and forgetting to wrap it in a closure; refactoring an eager collection into a lazy one and passing the old iterator; third-party stream/guzzle PSR-7 body generators.

Related errors


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

Appendix: source

Thrown at src/Illuminate/Collections/LazyCollection.php:54

     * @var (Closure(): \Generator<TKey, TValue, mixed, void>)|static|array<TKey, TValue>
     */
    public $source;

    /**
     * Create a new lazy collection instance.
     *
     * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>|(Closure(): \Generator<TKey, TValue, mixed, void>)|self<TKey, TValue>|array<TKey, TValue>|null  $source
     *
     * @throws \InvalidArgumentException
     */
    public function __construct($source = null)
    {
        if ($source instanceof Closure || $source instanceof self) {
            $this->source = $source;
        } elseif (is_null($source)) {
            $this->source = static::empty();
        } elseif ($source instanceof Generator) {
            throw new InvalidArgumentException(
                'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.'
            );
        } else {
            $this->source = $this->getArrayableItems($source);
        }
    }

    /**
     * Create a new instance of the collection.
     *
     * @param  \Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>|(Closure(): \Generator<TKey, TValue, mixed, void>)|self<TKey, TValue>|array<TKey, TValue>|null  $items
     * @return static
     */
    protected function newInstance($items = [])
    {
        return new static($items);
    }

View on GitHub (pinned to e0f6eb3518)