sebastianbergmann/phpunit · error · InvalidDataProviderException
Data set %s provided by %s is invalid, expected array but go
Error message
Data set %s provided by %s is invalid, expected array but got %s
What it means
PHPUnit called the data provider method named in your #[DataProvider] or #[DataProviderExternal] attribute and validated each data set it returned. Every value in a provider's iterable must be a plain array whose elements are passed as arguments to the test method. This error names the offending data set key, the provider (Class::method), and the actual type received instead of an array.
Source
Thrown at src/Metadata/Api/DataProvider.php:197
try {
foreach ($data as $key => $value) {
if (!is_int($key) && !is_string($key)) {
throw new InvalidDataProviderException(
sprintf(
'The key must be an integer or a string, %s given',
get_debug_type($key),
),
);
}
if ($key === '') {
throw new InvalidDataProviderException(
'The key must not be an empty string',
);
}
if (!is_array($value)) {
throw new InvalidDataProviderException(
sprintf(
'Data set %s provided by %s is invalid, expected array but got %s',
$this->formatKey($key),
$providerLabel,
get_debug_type($value),
),
);
}
if ($validateArgumentCount && $testMethodNumberOfParameters < count($value)) {
$this->triggerWarningForArgumentCount(
$testMethod,
$this->formatKey($key),
$providerLabel,
count($value),
$testMethodNumberOfParameters,
);
}View on GitHub (pinned to f123cdb2a2)
Solutions
- Open the provider named in the message and jump to the data set key it reports ("key" or #index).
- Wrap each row in an array so it maps onto test method parameters: return [[1, 2], [3, 4]]; or yield 'name' => [$value].
- If the test takes one parameter, keep each row a one-element array: [['hello'], ['world']].
- Re-run with --filter <testMethodName> to verify only this provider is affected.
Example fix
// before
public static function provider(): array
{
return ['hello', 'world'];
}
// after
public static function provider(): array
{
return [['hello'], ['world']];
} Defensive patterns
Strategy: validation
Validate before calling
// Meta-test: assert provider shape before PHPUnit consumes it
public function testProviderYieldsArgumentArrays(): void
{
foreach (MyTest::provider() as $key => $value) {
self::assertIsArray($value, sprintf('Data set %s is not an argument array', (string) $key));
}
} Type guard
function isValidProviderData(mixed $data): bool
{
if (!is_iterable($data)) {
return false;
}
foreach ($data as $key => $value) {
if ((!is_int($key) && !is_string($key)) || $key === '' || !is_array($value)) {
return false;
}
}
return true;
} Prevention
- Declare provider return types as array<int|string, array<mixed>> so PHPStan can flag scalar rows.
- Write one meta-test per provider asserting every row is an array with int/non-empty-string keys.
- Run vendor/bin/phpunit --list-tests in CI: it enumerates every provider and fails fast on malformed data.
When it happens
Trigger: A #[DataProvider('provider')] method returns an iterable containing at least one non-array value: return ['foo', 'bar']; (list of strings instead of list of argument arrays), yield 'user' => $userObject; (bare object row), or a generator that yields scalars.
Common situations: Providers migrated from frameworks where rows are single values; yield $key => $value instead of yield $key => [$value]; providers built from json_decode()/fixture files whose rows decode to scalars or objects; refactoring a single-parameter test and forgetting to wrap values.
Related errors
- The key "%s" has already been defined by provider %s
- Data Provider callable does not return an iterable
- Empty data set provided by data provider
- Subscriber "%s" does not implement any known interface - did
- Unknown event type "%s"
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/ba4aecbaa877c10b.
Report an issue: GitHub.