pestphp/pest · error · DatasetArgumentsMismatch

The test expects [%d] argument(s), but the dataset only prov

Error message

The test expects [%d] argument(s), but the dataset only provides [%d].

What it means

Before executing a dataset-driven test, Pest reflects on the test closure and counts its required parameters, then compares that with the number of values the dataset row supplies (also matching parameter names when the dataset uses named keys). If the row provides fewer values than required and the names do not cover the gap, Pest aborts the test with DatasetArgumentsMismatch instead of letting PHP raise an ArgumentCountError mid-test.

Source

Thrown at src/Concerns/Testable.php:498

        $testReflection = new ReflectionFunction($underlyingTest);
        $requiredParametersCount = $testReflection->getNumberOfRequiredParameters();
        $suppliedParametersCount = count($arguments);

        $datasetParameterNames = array_keys($arguments);
        $testParameterNames = array_map(
            fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(),
            array_filter($testReflection->getParameters(), fn (ReflectionParameter $reflectionParameter): bool => ! $reflectionParameter->isOptional()),
        );

        if (array_diff($testParameterNames, $datasetParameterNames) === []) {
            return;
        }

        if (isset($testParameterNames[0]) && $suppliedParametersCount >= $requiredParametersCount) {
            return;
        }

        throw new DatasetArgumentsMismatch($requiredParametersCount, $suppliedParametersCount);
    }

    /**
     * @throws Throwable
     */
    private function __callClosure(Closure $closure, array $arguments): mixed
    {
        return ExceptionTrace::ensure(fn (): mixed => call_user_func_array(Closure::bind($closure, $this, $this::class), $arguments));
    }

    public function preset(): Preset
    {
        return new Preset;
    }

    #[PostCondition]
    protected function __MarkTestIncompleteIfSnapshotHaveChanged(): void
    {

View on GitHub (pinned to 1af74a215c)

Solutions

  1. Make every dataset row supply all required parameters: ->with([[1, 2]]).
  2. Give the added parameters defaults so they become optional: function (int $a, int $b = 0).
  3. Use named keys in the dataset that exactly match the closure parameter names (['a' => 1, 'b' => 2]).
  4. If a dataset closure returns an array meant to be spread as multiple arguments, wrap it so Pest spreads rather than wraps: ->with(fn () => [[1, 2]]).

Example fix

// before
it('adds', function (int $a, int $b) {
    expect($a + $b)->toBe(3);
})->with([[1]]);
// after
it('adds', function (int $a, int $b) {
    expect($a + $b)->toBe(3);
})->with([[1, 2]]);
Defensive patterns

Strategy: validation

Validate before calling

// sanity-check dataset rows against the test signature before running:
$required = (new ReflectionFunction($testClosure))->getNumberOfRequiredParameters();
foreach ($dataset as $row) {
    $row = is_array($row) ? $row : [$row];
    if (count($row) < $required && array_diff($paramNames, array_keys($row)) !== []) {
        throw new RuntimeException('Dataset row provides '.count($row)." args, test requires {$required}");
    }
}

Prevention

When it happens

Trigger: it('adds', function (int $a, int $b) { ... })->with([[1]]); a named dataset row whose keys do not match the closure parameter names (['x' => 1] vs parameter $a) while also being short; a dataset closure returning a scalar that Pest wraps into a single-element array while the test expects two parameters.

Common situations: Adding a parameter to an existing test signature and forgetting to update every dataset row; datasets sourced from CSV/JSON/YAML files with missing columns; mixing named and positional datasets; refactoring datasets into generators that yield fewer fields.

Related errors


AI-assisted analysis of pestphp/pest@1af74a215c (2026-08-21). Data as JSON: /api/errors/73a89338a41c3774. Report an issue: GitHub.