sebastianbergmann/phpunit · error · InvalidDataProviderException

Data Provider callable does not return an iterable

Error message

Data Provider callable does not return an iterable

What it means

The test method uses a #[DataProviderClosure(fn () => ...)] attribute (a Closure as data provider). PHPUnit invoked the closure and requires its return value to be iterable (array or Traversable), because each entry becomes one data set. The closure returned something else, so the test errors out before any data set runs.

Source

Thrown at src/Metadata/Api/DataProvider.php:260

                    $e->getMessage(),
                    $e->getCode(),
                    $e,
                );
            }
        }

        foreach ($dataProviderClosure as $_dataProvider) {
            assert($_dataProvider instanceof DataProviderClosureMetadata);

            $providerLabel         = sprintf('callable provided to %s::%s()', $testClassName, $testMethod->getName());
            $validateArgumentCount = $testMethodIsNonVariadic && $_dataProvider->validateArgumentCount();

            try {
                $callable = $_dataProvider->closure();
                $data     = $callable();

                if (!is_iterable($data)) {
                    throw new InvalidDataProviderException(
                        'Data Provider callable does not return an iterable',
                    );
                }
            } catch (Throwable $e) {
                Event\Facade::emitter()->dataProviderMethodFinished(
                    $testMethodValueObject,
                    ...$methodsCalled,
                );

                throw InvalidDataProviderException::forException($e, $providerLabel);
            }

            foreach ($data as $key => $value) {
                if (!is_int($key) && !is_string($key)) {
                    Event\Facade::emitter()->dataProviderMethodFinished(
                        $testMethodValueObject,
                        ...$methodsCalled,
                    );

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Make the closure return an iterable of argument arrays: #[DataProviderClosure(fn () => [[1, 2], [3, 4]])].
  2. If the closure delegates to another function, verify that function's return value is iterable.
  3. Check for early exits that implicitly return null.

Example fix

// before
#[DataProviderClosure(fn () => null)]

// after
#[DataProviderClosure(fn () => [[1, 2], [3, 4]])]
Defensive patterns

Strategy: type-guard

Validate before calling

// Meta-test: the closure must return an iterable before wiring it into #[DataProviderClosure]
$closure = fn (): array => [[1, 2]];
self::assertIsIterable($closure());

Type guard

function returnsIterableDataProvider(Closure $provider): bool
{
    return is_iterable($provider());
}

Prevention

When it happens

Trigger: #[DataProviderClosure(fn () => null)] (implicit or explicit null return), fn () => 'rows' (string), or any closure returning a scalar or a non-Traversable object. Arrays and Generators are fine.

Common situations: Closures with an early return that implicitly returns null; refactoring a method provider into a closure and dropping the return statement; closures returning a single value instead of a list of argument arrays.

Related errors


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/82bbea2097a6b96c. Report an issue: GitHub.