{"record":{"id":"5ef5a74524b64785","repo":"sebastianbergmann/phpunit","slug":"not-enough-parameter-sets-configured-only-d-para-5ef5a7","errorCode":null,"errorMessage":"Not enough parameter sets configured, only %d parameter sets given for %s::%s()","messagePattern":"Not enough parameter sets configured, only (.+?) parameter sets given for (.+?)::(.+?)\\(\\)","errorType":"exception","errorClass":"NoMoreParameterSetsConfiguredException","httpStatus":null,"severity":"error","filePath":"src/Framework/MockObject/Runtime/Rule/UnorderedParameterSets.php","lineNumber":64,"sourceCode":"            if (!$parameters instanceof IndexedParameters) {\n                if (is_array($parameters)) {\n                    $parameters = new IndexedParameters(array_values($parameters), $index, false);\n                } else {\n                    $parameters = new IndexedParameters([$parameters], $index, false);\n                }\n            }\n\n            $this->stack[] = $parameters;\n        }\n\n        $this->unapplied                       = $this->stack;\n        $this->numberOfConfiguredParameterSets = count($stack);\n    }\n\n    public function apply(BaseInvocation $invocation): void\n    {\n        if ($this->unapplied === []) {\n            throw new NoMoreParameterSetsConfiguredException(\n                $invocation,\n                $this->numberOfConfiguredParameterSets,\n            );\n        }\n\n        $checkedParameters   = 0;\n        $unappliedParameters = count($this->unapplied);\n\n        while ($checkedParameters < $unappliedParameters) {\n            $checkedParameters++;\n            $parameters = array_shift($this->unapplied);\n\n            try {\n                $parameters->useAssertionCount(false);\n                $parameters->apply($invocation);\n\n                $this->applied[] = $parameters;\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Framework/MockObject/Runtime/Rule/UnorderedParameterSets.php#L46-L82","documentation":"This error is thrown by PHPUnit's MockObject component when a mocked method is invoked more times than you configured parameter sets for via $mock->expects(...)->withMatchedByOrder(...-independent lists) style unordered matching (UnorderedParameterSets, built from withConsecutive-replacement APIs such as the `sebastian/` unordered parameter set rule used by ->with() on matchers fed multiple sets). Each configured set is consumed once when an invocation matches it; when the mock is called again after every set has been applied, the invocation cannot be validated and PHPUnit fails immediately with NoMoreParameterSetsConfiguredException, telling you how many sets were originally configured.","triggerScenarios":"Calling a mocked method (e.g. $dependency->doSomething(...)) more times than the number of parameter sets registered on the expectation that uses unordered parameter sets. Each matching invocation shifts one set from the unapplied stack to the applied stack; the N+1-th call hits the empty-unapplied guard in UnorderedParameterSets::apply() and throws NoMoreParameterSetsConfiguredException($invocation, numberOfConfiguredParameterSets).","commonSituations":"A loop in the code under test iterates one more time than the test assumed (e.g. a retry, a batch with an extra element, a background tick); the test was written against an older behavior that made fewer calls; refactoring changed the number of calls while the expectation still lists the old sets; copy-pasting an expectation from another test that configured more sets.","solutions":["Compare the actual number of calls (add ->shouldReceive style count or inspect a var_dump of calls) with the number of parameter sets you configured, and add the missing set(s) matching the extra invocation's arguments.","If the extra calls are legitimate but uninteresting, loosen the expectation: move the parameter matching off the mock's with()-rule (e.g. use a callback/constraint that accepts repeated argument groups) or use returnValueMap()/callback stubbing instead of a fixed list of unordered sets.","If the extra call is a bug in the code under test, keep the expectation and fix the production code so it stops making the superfluous call.","As a last resort, restrict which methods the partial mock intercepts (onlyMethods()/addMethods()) so the over-called method runs the real implementation instead of the exhausted expectation."],"exampleFix":"// before\n$logger->expects($this->atLeast(2))\n       ->method('log')\n       ->withMatchedByOrder(\n           ['info', 'start'],\n           ['info', 'finish'],\n       );\n// code under test now logs a third line -> NoMoreParameterSetsConfiguredException\n\n// after\n$logger->expects($this->atLeast(3))\n       ->method('log')\n       ->withMatchedByOrder(\n           ['info', 'start'],\n           ['info', 'finish'],\n           ['debug', 'cleanup'],   // set for the extra invocation\n       );","handlingStrategy":"validation","validationCode":"// Before the SUT runs, make sure the expectation covers the call count:\n// prefer self-describing stubs when the number of calls is not fixed.\n$logger->method('log')->willReturnCallback(\n    fn (string $level, string $message) => match ($level) {\n        'info'  => true,\n        default => true, // any extra call is fine\n    },\n);","typeGuard":null,"tryCatchPattern":"// Inside a test you normally let the exception fail the test. Only catch it to\n// convert it into an assertion failure with extra context:\nuse PHPUnit\\Framework\\MockObject\\NoMoreParameterSetsConfiguredException;\n\ntry {\n    $sut->run();\n} catch (NoMoreParameterSetsConfiguredException $e) {\n    $this->fail('Mock called more often than configured parameter sets: ' . $e->getMessage());\n}","preventionTips":["Derive the number of parameter sets from the same data/count the SUT iterates, not from a hardcoded literal.","Prefer argument-based stubbing (willReturnCallback/returnValueMap) over ordered/unordered set lists when call order or count is not strictly part of the contract under test.","Add a terminating no-op call or explicit final expectation when the SUT loops until a sentinel value.","When a retry count changes in production code, grep tests for willReturnOnConsecutiveCalls and matching set lists to update them in the same commit."],"tags":["phpunit","mockobject","expectation","parameter-sets","test-failure"],"backgroundTag":"mock-expectation-not-met","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}