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

  1. Remove the explicit set: drop ->withSets([SetList::DEAD_CODE]) and keep ->withDeadCodeLevel(20)
  2. Or drop the level method and keep the set import -- the exception message names both so delete exactly one side
  3. 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

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


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/e3b504dd9c691285. Report an issue: GitHub.