sebastianbergmann/phpunit · error · InvalidDataProviderException

The key "%s" has already been defined by %s

Error message

The key "%s" has already been defined by %s

What it means

The test method carries multiple #[TestWith] / #[TestWithJson] attributes, and PHPUnit keys those data sets by the optional name: argument so names appear in test output. Two attributes on the same test method declared the same name; the message names the attribute index (TestWith#N) that registered it first.

Source

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

    {
        $result                       = [];
        $testMethodNumberOfParameters = $testMethod->getNumberOfParameters();
        $testMethodIsNonVariadic      = !$testMethod->isVariadic();
        $autoIncrementKey             = 0;

        foreach ($testWith as $i => $_testWith) {
            assert($_testWith instanceof TestWith);

            $providerLabel = sprintf('TestWith#%s attribute', $i);
            $value         = $_testWith->data();

            if ($_testWith->hasName()) {
                $key = $_testWith->name();

                assert($key !== null);

                if (array_key_exists($key, $result)) {
                    throw new InvalidDataProviderException(
                        sprintf(
                            'The key "%s" has already been defined by %s',
                            $key,
                            $result[$key]->label(),
                        ),
                    );
                }

                $formattedKey = $this->formatKey($key);
            } else {
                $key          = null;
                $formattedKey = $this->formatKey($autoIncrementKey);
            }

            if (!is_array($value)) {
                throw new InvalidDataProviderException(
                    sprintf(
                        'Data set %s provided by %s is invalid, expected array but got %s',

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Rename one of the duplicated name: values on that test method.
  2. Or drop name: entirely — unnamed #[TestWith] data sets are auto-numbered and never collide.

Example fix

// before
#[TestWith([1], name: 'one')]
#[TestWith([2], name: 'one')]

// after
#[TestWith([1], name: 'one')]
#[TestWith([2], name: 'two')]
Defensive patterns

Strategy: validation

Validate before calling

// Scan a test method's TestWith attributes for duplicate names before running
$method = new ReflectionMethod(MyTest::class, 'testX');
$names = [];
foreach ($method->getAttributes(TestWith::class) as $attribute) {
    $name = $attribute->newInstance()->name();
    if ($name !== null) {
        self::assertArrayNotHasKey($name, $names, "Duplicate TestWith name '$name'");
        $names[$name] = true;
    }
}

Prevention

When it happens

Trigger: Two #[TestWith([...], name: 'edge-case')] attributes, or a #[TestWith] plus a #[TestWithJson], on the same test method using the same name:.

Common situations: Copy-pasting an attribute and forgetting to update name:; combining named and JSON variants with generic names like 'data' or 'test'.

Related errors


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