{"record":{"id":"efc802aaeac89664","repo":"rectorphp/rector","slug":"pick-only-one-php-version-target-in-withdowngrade","errorCode":null,"errorMessage":"Pick only one PHP version target in \"withDowngradeSets()\". All rules down to this version will be used.","messagePattern":"Pick only one PHP version target in \"withDowngradeSets\\(\\)\"\\. All rules down to this version will be used\\.","errorType":"exception","errorClass":"InvalidConfigurationException","httpStatus":null,"severity":"error","filePath":"src/Configuration/RectorConfigBuilder.php","lineNumber":773,"sourceCode":"        $this->registerServices[] = new RegisteredService($className, $alias, $tag);\n        return $this;\n    }\n    /**\n     * DX helper\n     * @see https://getrector.com/documentation/creating-a-node-visitor\n     * @param class-string $decoratingNodeVisitorClass\n     */\n    public function registerDecoratingNodeVisitor(string $decoratingNodeVisitorClass): self\n    {\n        Assert::isAOf($decoratingNodeVisitorClass, NodeVisitor::class);\n        $this->registerServices[] = new RegisteredService($decoratingNodeVisitorClass, null, DecoratingNodeVisitorInterface::class);\n        return $this;\n    }\n    public function withDowngradeSets(bool $php84 = \\false, bool $php83 = \\false, bool $php82 = \\false, bool $php81 = \\false, bool $php80 = \\false, bool $php74 = \\false, bool $php73 = \\false, bool $php72 = \\false, bool $php71 = \\false): self\n    {\n        $pickedDowngradeSets = array_keys(array_filter([DowngradeLevelSetList::DOWN_TO_PHP_84 => $php84, DowngradeLevelSetList::DOWN_TO_PHP_83 => $php83, DowngradeLevelSetList::DOWN_TO_PHP_82 => $php82, DowngradeLevelSetList::DOWN_TO_PHP_81 => $php81, DowngradeLevelSetList::DOWN_TO_PHP_80 => $php80, DowngradeLevelSetList::DOWN_TO_PHP_74 => $php74, DowngradeLevelSetList::DOWN_TO_PHP_73 => $php73, DowngradeLevelSetList::DOWN_TO_PHP_72 => $php72, DowngradeLevelSetList::DOWN_TO_PHP_71 => $php71]));\n        if (count($pickedDowngradeSets) !== 1) {\n            throw new InvalidConfigurationException('Pick only one PHP version target in \"withDowngradeSets()\". All rules down to this version will be used.');\n        }\n        $this->sets[] = $pickedDowngradeSets[0];\n        return $this;\n    }\n    public function withRealPathReporting(bool $absolutePath = \\true): self\n    {\n        $this->reportingRealPath = $absolutePath;\n        return $this;\n    }\n    /**\n     * Report skips configured via withSkip() that never matched anything during the run,\n     * so they can be safely removed.\n     */\n    public function reportUnusedSkips(bool $report = \\true): self\n    {\n        $this->reportUnusedSkips = $report;\n        return $this;\n    }","sourceCodeStart":755,"sourceCodeEnd":791,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/src/Configuration/RectorConfigBuilder.php#L755-L791","documentation":"RectorConfigBuilder::withDowngradeSets() accepts nine boolean flags (php84 down to php71) and resolves exactly one target level via array_filter/array_keys. Downgrade rule sets are cumulative, so one target automatically includes every set above it; passing zero flags or more than one true flag is ambiguous and throws InvalidConfigurationException before any set is registered.","triggerScenarios":"Calling withDowngradeSets() with no arguments, or with two or more flags set to true, e.g. withDowngradeSets(php82: true, php80: true). The count($pickedDowngradeSets) !== 1 check fires on both 0 and >= 2 selections.","commonSituations":"Migrating a config from withPhpSets() (where named arguments imply one version) to the downgrade API and enabling several levels 'to be safe'; or an editor/rector-upgrade tool commenting flags in and leaving none enabled.","solutions":["Enable only the LOWEST PHP version you must support, e.g. withDowngradeSets(php80: true); every rule down to that version is included automatically","Remove the extra true flags one by one until exactly one remains","If you genuinely need several independent levels, use withSets([...]) with explicit DowngradeLevelSetList constants instead","Run vendor/bin/rector and confirm the InvalidConfigurationException no longer triggers before committing"],"exampleFix":"// before\nreturn $rectorConfigBuilder->withDowngradeSets(php82: true, php80: true);\n\n// after: one target; sets above PHP 8.0 are included automatically\nreturn $rectorConfigBuilder->withDowngradeSets(php80: true);","handlingStrategy":"validation","validationCode":"// before building config: exactly one downgrade flag may be true\n$flags = ['php84' => $php84, 'php83' => $php83, 'php82' => $php82, 'php81' => $php81,\n    'php80' => $php80, 'php74' => $php74, 'php73' => $php73, 'php72' => $php72, 'php71' => $php71];\nif (count(array_filter($flags)) !== 1) {\n    throw new InvalidArgumentException('withDowngradeSets(): enable exactly one (the lowest) PHP target.');\n}\n$builder->withDowngradeSets(...array_filter($flags));","typeGuard":null,"tryCatchPattern":"try {\n    $builder->withDowngradeSets(php80: true);\n} catch (InvalidConfigurationException $e) {\n    // surface to the user editing rector.php with the offending flags\n    $logger->error($e->getMessage());\n}","preventionTips":["Treat the lowest supported PHP version as the single source of truth in one env/parameter, then set only that flag","Add a config smoke test that instantiates the builder and runs withDowngradeSets() with your production flags","Remember downgrade sets are cumulative — enabling more than one target is always redundant, never additive"],"tags":["php","rector","config","downgrade","php-version"],"backgroundTag":"config-validation-failed","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}