rectorphp/rector · error · InvalidConfigurationException
Your config already enables %s set.%sRemove "->%s()" as it o
Error message
Your config already enables %s set.%sRemove "->%s()" as it only duplicates it, or remove %s set.
What it means
Besides the withPhp*() guard, RectorConfigBuilder maps each level method (withTypeCoverageLevel, withTypeCoverageDocblockLevel, withDeadCodeLevel, withCodeQualityLevel, withCodingStyleLevel) to the set it implicitly enables. If you both call a level method and load the same set (e.g. via withSets([SetList::DEAD_CODE]) or importing the set file), it throws InvalidConfigurationException naming both the method and the set, since the rules would run twice.
Source
Thrown at src/Configuration/RectorConfigBuilder.php:188
$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);
}
// must be in upper part, as these services might be used by rule registered bellow
foreach ($this->registerServices as $registerService) {
$rectorConfig->singleton($registerService->getClassName());
if ($registerService->getAlias()) {
$rectorConfig->alias($registerService->getClassName(), $registerService->getAlias());
}
if ($registerService->getTag()) {
$rectorConfig->tag($registerService->getClassName(), $registerService->getTag());View on GitHub (pinned to 408fcb0ff1)
Solutions
- Remove the explicit set: drop ->withSets([SetList::DEAD_CODE]) and keep ->withDeadCodeLevel(20)
- Or drop the level method and keep the set import -- the exception message names both so delete exactly one side
- Audit rector.php for every SetList::* constant that overlaps a with*Level() call
Example fix
// before
return RectorConfig::configure()
->withSets([SetList::DEAD_CODE])
->withDeadCodeLevel(20);
// after
return RectorConfig::configure()
->withDeadCodeLevel(20); Defensive patterns
Strategy: validation
Validate before calling
// lint: a level method plus its matching SetList constant is a duplicate
$config = file_get_contents('rector.php');
$duplicates = [
'withDeadCodeLevel' => 'SetList::DEAD_CODE',
'withCodeQualityLevel' => 'SetList::CODE_QUALITY',
'withCodingStyleLevel' => 'SetList::CODING_STYLE',
'withTypeCoverageLevel' => 'SetList::TYPE_DECLARATION',
];
foreach ($duplicates as $method => $set) {
if (strpos($config, $method) !== false && strpos($config, $set) !== false) {
fwrite(STDERR, "Duplicate enablement: {$method} + {$set}\n");
}
} Try / catch
catch \Rector\Exception\Configuration\InvalidConfigurationException; parse the set title and method name from the message and remove one side, then re-load the config.
Prevention
- Enable a domain exactly once: either the with*Level() method or the SetList entry, never both
- Review imports in rector.php for set files that overlap builder methods
- Keep configs small and re-run `rector init` diff after major upgrades to adopt current idioms
When it happens
Trigger: rector.php containing ->withDeadCodeLevel(20) together with ->withSets([SetList::DEAD_CODE]), or the same duplication for type declarations / code quality / coding style sets.
Common situations: Configs assembled from older examples that manually imported sets plus newer snippets using the level builder methods; enabling 'dead-code' via a Symfony-ish bundle config and also via the builder.
Related errors
- Method "%s()" can be called only once. It always includes al
- "%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/e3b504dd9c691285.
Report an issue: GitHub.