rectorphp/rector · error · InvalidConfigurationException
Your config uses "withPhp*()" and "withPhpLevel()" methods a
Error message
Your config uses "withPhp*()" and "withPhpLevel()" methods at the same time.%sPick one of them to avoid rule conflicts.
What it means
RectorConfigBuilder applies your config lazily and cross-checks flags: if both withPhpLevel() and any withPhp*() sets method (withPhpSets/withPhpSets() variants) were called, it throws InvalidConfigurationException, because each independently builds the PHP upgrade rule list and combining them causes conflicting, duplicated rules. Pick exactly one strategy for PHP-version upgrades.
Source
Thrown at src/Configuration/RectorConfigBuilder.php:179
{
if ($this->setGroups !== [] || $this->setProviders !== []) {
$setProviderCollector = new SetProviderCollector(array_map(\Closure::fromCallable([$rectorConfig, 'make']), \array_keys($this->setProviders)));
$setManager = new SetManager($setProviderCollector, new InstalledPackageResolver(getcwd()));
$this->groupLoadedSets = $setManager->matchBySetGroups($this->setGroups);
SimpleParameterProvider::addParameter(\Rector\Configuration\Option::COMPOSER_BASED_SETS, $this->groupLoadedSets);
}
// not to miss it by accident
if ($this->isWithPhpSetsUsed === \true) {
$this->sets[] = SetList::PHP_POLYFILLS;
}
if ($this->pickedPhpSetsVersion !== null) {
SimpleParameterProvider::setParameter(\Rector\Configuration\Option::POLYFILL_CEILING_PHP_VERSION, $this->pickedPhpSetsVersion);
}
// merge sets together
$this->sets = array_merge($this->sets, $this->groupLoadedSets);
$uniqueSets = array_unique($this->sets);
if ($this->isWithPhpLevelUsed && $this->isWithPhpSetsUsed) {
throw new InvalidConfigurationException(sprintf('Your config uses "withPhp*()" and "withPhpLevel()" methods at the same time.%sPick one of them to avoid rule conflicts.', \PHP_EOL));
}
foreach (self::LEVEL_METHOD_TO_SET as $levelMethod => [$setFilePath, $setTitle]) {
if (!isset($this->usedLevelMethods[$levelMethod])) {
continue;
}
if (!in_array($setFilePath, $uniqueSets, \true)) {
continue;
}
throw new InvalidConfigurationException(sprintf('Your config already enables %s set.%sRemove "->%s()" as it only duplicates it, or remove %s set.', $setTitle, \PHP_EOL, $levelMethod, $setTitle));
}
if ($uniqueSets !== []) {
$rectorConfig->sets($uniqueSets);
}
// log rules from sets and compare them with explicit rules
$setRegisteredRectorClasses = $rectorConfig->getMainRectorClasses();
SimpleParameterProvider::addParameter(\Rector\Configuration\Option::SET_REGISTERED_RULES, $setRegisteredRectorClasses);
if ($this->paths !== []) {
$rectorConfig->paths($this->paths);View on GitHub (pinned to 408fcb0ff1)
Solutions
- Keep ->withPhpSets() (no args picks the version from composer.json) and delete the withPhpLevel() line
- Or keep ->withPhpLevel(n) and remove every withPhp*() call
- Re-generate a baseline config: vendor/bin/rector init and merge your custom rules into it
Example fix
// before
return RectorConfig::configure()
->withPhpSets(php82: true)
->withPhpLevel(82);
// after
return RectorConfig::configure()
->withPhpSets(); // version resolved from composer.json Defensive patterns
Strategy: validation
Validate before calling
// static guard in a config lint step: forbid mixing the two APIs
$config = file_get_contents('rector.php');
if (preg_match('/withPhpLevel\s*\(/', $config) && preg_match('/withPhpSets\s*\(/', $config)) {
fwrite(STDERR, "rector.php mixes withPhpLevel() and withPhpSets(); pick one\n");
exit(1);
} Try / catch
catch \Rector\Exception\Configuration\InvalidConfigurationException in config-loading tests; assert your checked-in rector.php never triggers it.
Prevention
- Standardize on withPhpSets() with no arguments (composer.json-driven) in all projects
- Delete withPhpLevel() lines during upgrade migrations instead of commenting them out
- Run `vendor/bin/rector process src --dry-run` in CI so config conflicts fail fast
When it happens
Trigger: A rector.php that contains both ->withPhpLevel(82) and ->withPhpSets() (or withPhpSets(php82: true)), often left over after migrating from the old level API to the newer sets API.
Common situations: Merging an old config snippet (withPhpLevel) with a freshly generated one (withPhpSets) during upgrades; copy-pasting examples from different Rector eras into one rector.php.
Related errors
- Pick only one version target in "withPhpSets()". All rules u
- Pick only one PHP version target in "withDowngradeSets()". A
- The "->withPhpSets()" method uses named arguments. Its suita
- "%s" rule is deprecated, as inverting nested ifs to early re
- "%s" rule is deprecated, as splitting a single condition int
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/89f78cd900fbab98.
Report an issue: GitHub.