rectorphp/rector · error · InvalidConfigurationException
Method "%s()" can be called only once. It always includes al
Error message
Method "%s()" can be called only once. It always includes all previous sets UP TO the defined version.%sThe best practise is to call it once with no argument. That way it will pick up PHP version from composer.json and your project will always stay up to date.
What it means
withPhpSets() is designed to be called once: without arguments it picks the target PHP version from composer.json and includes all sets up to it, so a second call would either duplicate or contradict the first. RectorConfigBuilder tracks isWithPhpSetsUsed and throws InvalidConfigurationException (naming RectorConfigBuilder::withPhpSets) on the second invocation. The message recommends the zero-argument form so the project tracks composer.json automatically.
Source
Thrown at src/Configuration/RectorConfigBuilder.php:409
bool $php81 = \false,
bool $php80 = \false,
bool $php74 = \false,
bool $php73 = \false,
bool $php72 = \false,
bool $php71 = \false,
bool $php70 = \false,
bool $php56 = \false,
bool $php55 = \false,
bool $php54 = \false,
bool $php53 = \false,
// place on later as BC break when used in php 7.x without named arg
bool $php84 = \false,
bool $php85 = \false,
bool $php86 = \false
): self
{
if ($this->isWithPhpSetsUsed === \true) {
throw new InvalidConfigurationException(sprintf('Method "%s()" can be called only once. It always includes all previous sets UP TO the defined version.%sThe best practise is to call it once with no argument. That way it will pick up PHP version from composer.json and your project will always stay up to date.', __METHOD__, \PHP_EOL));
}
$this->isWithPhpSetsUsed = \true;
$pickedPhpVersions = array_keys(array_filter([PhpVersion::PHP_53 => $php53, PhpVersion::PHP_54 => $php54, PhpVersion::PHP_55 => $php55, PhpVersion::PHP_56 => $php56, PhpVersion::PHP_70 => $php70, PhpVersion::PHP_71 => $php71, PhpVersion::PHP_72 => $php72, PhpVersion::PHP_73 => $php73, PhpVersion::PHP_74 => $php74, PhpVersion::PHP_80 => $php80, PhpVersion::PHP_81 => $php81, PhpVersion::PHP_82 => $php82, PhpVersion::PHP_83 => $php83, PhpVersion::PHP_84 => $php84, PhpVersion::PHP_85 => $php85, PhpVersion::PHP_86 => $php86]));
if ($pickedPhpVersions !== []) {
Notifier::errorWithPhpSetsNotSuitableForPHP74AndLower();
}
if (count($pickedPhpVersions) > 1) {
throw new InvalidConfigurationException(sprintf('Pick only one version target in "withPhpSets()". All rules up to this version will be used.%sTo use your composer.json PHP version, keep arguments empty.', \PHP_EOL));
}
// no version picked, resolve it from the project composer.json
if ($pickedPhpVersions === []) {
return $this->addPhpLevelSets(ComposerJsonPhpVersionResolver::resolveFromCwdOrFail());
}
// explicitly picked version is a ceiling, even for polyfilled rules
$this->pickedPhpSetsVersion = $pickedPhpVersions[0];
return $this->addPhpLevelSets($pickedPhpVersions[0]);
}
public function withPhp53Sets(): selfView on GitHub (pinned to 408fcb0ff1)
Solutions
- Keep a single call, ideally ->withPhpSets() with no arguments so the version follows composer.json
- If two versions were attempted, decide the real target and delete the other call
- If a shared imported config calls withPhpSets(), remove the call from the main file (or vice versa)
Example fix
// before
return RectorConfig::configure()
->withPhpSets(php74: true)
->withPhpSets(php82: true);
// after
return RectorConfig::configure()
->withPhpSets(); // single call, version from composer.json Defensive patterns
Strategy: validation
Validate before calling
// lint: withPhpSets must appear at most once (imports included)
$occurrences = substr_count(file_get_contents('rector.php'), 'withPhpSets(');
$imported = glob(__DIR__ . '/config/*.php') ?: []; // if you re-export sub-configs, count those too
foreach ($imported as $file) {
$occurrences += substr_count((string) file_get_contents($file), 'withPhpSets(');
}
if ($occurrences > 1) {
fwrite(STDERR, "withPhpSets() called {$occurrences} times; call it once\n");
} Try / catch
catch \Rector\Exception\Configuration\InvalidConfigurationException naming withPhpSets; deduplicate to a single call (prefer zero-argument) and reload.
Prevention
- Keep exactly one ->withPhpSets() call, ideally without arguments
- Shared/imported config files must not also call withPhpSets()
- During merges, grep for 'withPhpSets' to catch duplicated blocks
When it happens
Trigger: Two ->withPhpSets(...) calls in one rector.php, e.g. ->withPhpSets(php82: true) earlier and ->withPhpSets(php74: true) added later, or an imported sub-config that also calls withPhpSets().
Common situations: Merging configs during refactors; importing a shared config file that itself calls withPhpSets() while the main rector.php also does.
Related errors
- Your config already enables %s set.%sRemove "->%s()" as it o
- "%s" rule is deprecated, as inverting nested ifs to early re
- "%s" rule is deprecated, as splitting a single condition int
- "%s" rule is deprecated, as splitting a single return into m
- "%s" is deprecated as risky change with little value. Use ma
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/3211f8db6be41c1c.
Report an issue: GitHub.