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
- Pass a closure that returns the generator: new LazyCollection(fn () => myGeneratorFunc()).
- If the source is already iterable, pass it directly (array, Arrayable) instead of a generator.
- 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
- Always pass fn () => generatorFunc() rather than generatorFunc().
- Static-analyse with PHPStan/Larastan to catch Generator-vs-Closure mismatches early.
- Document the source parameter contract clearly in your own wrappers.
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
- Number of groups must be at least 1.
- Size value must be at least 1.
- Step value cannot be zero.
- Step value must be at least 1.
- A driver must be specified.
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)