rectorphp/rector · error · ShouldNotHappenException
These rules from "$rectorConfig->skip()" do not exist - remo
Error message
These rules from "$rectorConfig->skip()" do not exist - remove them or fix their names:
What it means
Rector validates every entry passed to RectorConfig::skip() (src/Config/RectorConfig.php:126) and throws a ShouldNotHappenException listing all skip entries that are not existing classes. The skip list is matched against real Rector rules, paths and class names, so a typo'd or renamed rule name can never match anything and would silently disable the skip - Rector refuses to run instead of ignoring your intent. This check runs when the rector.php config is loaded, before analysis starts.
Source
Thrown at src/Validation/RectorConfigValidator.php:63
if (!self::isRectorClassValue($value)) {
continue;
}
if (class_exists($value)) {
$skippedRectorRules[] = $value;
continue;
}
$nonExistingRules[] = $value;
}
SimpleParameterProvider::addParameter(Option::SKIPPED_RECTOR_RULES, $skippedRectorRules);
SimpleParameterProvider::addParameter(Option::SKIPPED_NON_RECTOR_CLASSES, $skippedNonRectorClasses);
if ($nonExistingRules === []) {
return;
}
$nonExistingRulesString = '';
foreach ($nonExistingRules as $nonExistingRule) {
$nonExistingRulesString .= ' * ' . $nonExistingRule . \PHP_EOL;
}
throw new ShouldNotHappenException('These rules from "$rectorConfig->skip()" do not exist - remove them or fix their names:' . \PHP_EOL . $nonExistingRulesString);
}
/**
* Only Rector rules are matched against skipped classes, so any other class can never be skipped
*/
private static function isNonRectorClass(string $key): bool
{
// interfaces are allowed, as they can mark a group of Rector rules
if (!class_exists($key)) {
return \false;
}
if (is_a($key, RectorInterface::class, \true)) {
return \false;
}
return !is_a($key, PostRectorInterface::class, \true);
}
/**
* @param mixed $value
*/View on GitHub (pinned to 408fcb0ff1)
Solutions
- Read the exception message: it lists every non-existing entry with ' * ' prefix; remove those entries from skip() or correct their spelling to the real FQCN.
- If the rule was renamed in a newer version, find the new class name (vendor/rector/rector-src, your IDE, or the vendor package changelog) and update the skip entry to the new class-string.
- Verify each remaining entry resolves: run a quick check like `php -r "var_dump(class_exists($argv[1], true) || interface_exists($argv[1], true));" SomeRectorClass` for each skip entry.
- Pin or align versions: if the skip entries come from a ruleset package, make sure your rector/rector version matches the one that package expects, so the skipped class names exist again.
Example fix
// before
$rectorConfig->skip([
Rectify\TypeDeclaration\Rector\Property\TypedPropertyRector::class, // typo + wrong namespace
Rector\CodeQuality\Rector\If_\CombinedIfRector::class,
]);
// after
$rectorConfig->skip([
Rector\CodeQuality\Rector\If_\CombinedIfRector::class,
]); Defensive patterns
Strategy: validation
Validate before calling
// before calling $rectorConfig->skip() in rector.php
$skip = [
Rector\CodeQuality\Rector\If_\CombinedIfRector::class,
'Rector\TypeDeclaration\Rector\Property\MaybeTypedRector', // typo, would throw
];
$nonExisting = array_filter($skip, function ($entry): bool {
if (!is_string($entry)) {
return false; // path or fn-style entries, not class names
}
return !class_exists($entry) && !interface_exists($entry);
});
if ($nonExisting !== []) {
throw new InvalidArgumentException('Skip entries do not exist: ' . implode(', ', $nonExisting));
}
$rectorConfig->skip($skip); Type guard
function skipEntriesExist(array $skip): bool
{
foreach ($skip as $entry) {
if (is_string($entry) && !class_exists($entry) && !interface_exists($entry)) {
return false;
}
}
return true;
} Prevention
- Always use ::class constants instead of quoted strings for rule names in skip(), so renames surface as PHP fatal errors in the owning file instead of Rector validation errors.
- After every `composer update` of rector/rector or a ruleset package, run `vendor/bin/rector process --dry-run` on a tiny path to catch stale skip entries early in CI.
- Delete skip entries in the same commit that removes/renames the rule they reference.
When it happens
Trigger: Calling $rectorConfig->skip([...]) with a misspelled class name (e.g. 'TypeDeclarationRector' instead of the real FQCN); skipping a rule class that was renamed or removed in a newer rector/rector or vendor package version; skipping by constant from an old SetList (e.g. SetList::DEAD_CODE string value) instead of a rule class name; entries left over after the rule they excluded was deleted from the codebase.
Common situations: Upgrading rector/rector (or a ruleset package) to a major version where rule classes were renamed, and old skip entries no longer resolve; copy-pasting skip lines from blog posts or another project's rector.php that reference rules your installed version does not contain; CI suddenly failing after composer update although nothing in the repo changed, because the vendor rule class disappeared; refactoring where the excluded rule was deleted and the skip entry was forgotten.
Related errors
- "%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
- Bootstrap file "%s" does not exist.
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/1457aaffb06362a2.
Report an issue: GitHub.