sebastianbergmann/phpunit · error · InvalidDataProviderException
The key "%s" has already been defined by provider %s
Error message
The key "%s" has already been defined by provider %s
What it means
PHPUnit merges the data sets of every provider attached to a test method into one keyed result (integer keys are appended and never collide; string keys are registered by name). While iterating a #[DataProvider]/#[DataProviderExternal] method's data, a string key was found that another provider (or an earlier row of the same provider) had already registered. The message names the duplicate key and the provider label that defined it first.
Source
Thrown at src/Metadata/Api/DataProvider.php:224
if ($validateArgumentCount && $testMethodNumberOfParameters < count($value)) {
$this->triggerWarningForArgumentCount(
$testMethod,
$this->formatKey($key),
$providerLabel,
count($value),
$testMethodNumberOfParameters,
);
}
if (is_int($key)) {
$result[] = new ProvidedData($providerLabel, $value);
continue;
}
if (array_key_exists($key, $result)) {
throw new InvalidDataProviderException(
sprintf(
'The key "%s" has already been defined by provider %s',
$key,
$result[$key]->label(),
),
);
}
$result[$key] = new ProvidedData($providerLabel, $value);
}
} catch (Throwable $e) {
Event\Facade::emitter()->dataProviderMethodFinished(
$testMethodValueObject,
...$methodsCalled,
);
throw new InvalidDataProviderException(
$e->getMessage(),View on GitHub (pinned to f123cdb2a2)
Solutions
- Rename the duplicate key in whichever provider you control so it is unique across ALL providers attached to that test method (the message names the first definer).
- If two providers on one test method intentionally use the same keys, merge them into a single provider.
- Or drop the string keys and yield without a key — integer keys are appended and cannot collide.
Example fix
// before: both providers on testX return key 'valid'
#[DataProvider('stringProvider')]
#[DataProvider('numberProvider')]
public function testX(string $s, int $n): void {}
// after: make string keys unique across providers
// stringProvider(): ['validString' => ['a', 1]]
// numberProvider(): ['validNumber' => ['b', 2]] Defensive patterns
Strategy: validation
Validate before calling
// Meta-test: keys must be unique across all providers attached to one method
$all = [];
foreach ([MyTest::stringProvider(), MyTest::numberProvider()] as $provider) {
foreach ($provider as $key => $_) {
if (is_string($key)) {
self::assertArrayNotHasKey($key, $all, "Duplicate provider key '$key'");
$all[$key] = true;
}
}
} Type guard
function hasUniqueProviderKeys(iterable ...$providers): bool
{
$seen = [];
foreach ($providers as $provider) {
foreach ($provider as $key => $_) {
if (is_string($key) && isset($seen[$key])) {
return false;
}
if (is_string($key)) {
$seen[$key] = true;
}
}
}
return true;
} Prevention
- Prefix provider keys by domain ('email_valid', 'csv_valid') when several providers serve one test method.
- Prefer keyless data sets (auto-numbered) when names add no value.
- Add a meta-test asserting unique keys across all providers attached to the same method.
When it happens
Trigger: The same string key appears twice in one provider's return value, or two #[DataProvider] attributes on one test method produce overlapping string keys (e.g. both return a 'valid' row). The result array is shared across all providers attached to the method.
Common situations: Copy-pasting a provider onto the same test method; merging providers whose generic keys ('ok', 'valid', 'empty') overlap; combining fixture files without deduplicating keys.
Related errors
- Data set %s provided by %s is invalid, expected array but go
- Data Provider callable does not return an iterable
- Empty data set provided by data provider
- The key "%s" has already been defined by %s
- Subscriber "%s" does not implement any known interface - did
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/37cd2be61a5412cc.
Report an issue: GitHub.