rectorphp/rector · error · InvalidConfigurationException

Pick only one version target in "withPhpSets()". All rules u

Error message

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.

What it means

withPhpSets() accepts one boolean per PHP version (php53..php86); passing more than one true flag is contradictory because the method means 'everything up to this version'. RectorConfigBuilder collects the true flags via array_filter and throws InvalidConfigurationException when count > 1. To target the composer.json version, pass no flags at all.

Source

Thrown at src/Configuration/RectorConfigBuilder.php:417

        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(): self
    {
        return $this->reportDeprecatedPhpSetsMethod(__FUNCTION__);
    }
    public function withPhp54Sets(): self
    {
        return $this->reportDeprecatedPhpSetsMethod(__FUNCTION__);
    }
    public function withPhp55Sets(): self

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Keep only the highest target flag: ->withPhpSets(php83: true)
  2. Better: call ->withPhpSets() with no arguments and let composer.json's php requirement define the ceiling
  3. Note the flags are cumulative by design -- you never need two to chain versions

Example fix

// before
return RectorConfig::configure()
    ->withPhpSets(php82: true, php83: true);

// after
return RectorConfig::configure()
    ->withPhpSets(php83: true);  // includes everything up to 8.3
Defensive patterns

Strategy: validation

Validate before calling

// lint: exactly zero or one version flag in withPhpSets
if (preg_match('/withPhpSets\(([^)]*)\)/', $configSrc, $m)
    && preg_match_all('/php\d+:\s*true|(?<!:)\btrue\b/', $m[1], $flags) > 1
) {
    fwrite(STDERR, "withPhpSets() got multiple version flags; keep only the highest\n");
}

Try / catch

catch \Rector\Exception\Configuration\InvalidConfigurationException; on the 'Pick only one version target' message, keep the single highest flag and re-run.

Prevention

When it happens

Trigger: Calling ->withPhpSets(php82: true, php83: true), or adding a new version flag while forgetting to remove the old one; also older snippets using positional booleans (php74: true) combined with newer flags.

Common situations: Incremental upgrades where a developer adds the next version instead of replacing the previous flag; copy-pasted examples with different targets merged.

Related errors


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