rectorphp/rector · error · ShouldNotHappenException
"%s" rule is deprecated, as too niche; use a custom rule sco
Error message
"%s" rule is deprecated, as too niche; use a custom rule scoped to your own trait and interface pair instead
What it means
Rector\Transform\Rector\Class_\AddInterfaceByTraitRector is deprecated: refactor() throws Rector\Exception\ShouldNotHappenException on the first Class_ node. The rule added a configured interface to every class that used a configured trait, but that trait-to-interface pairing is too niche to ship generically. The configure() body is an empty no-op, so old trait/interface configuration is silently ignored before the exception fires.
Source
Thrown at rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php:47
{
use SomeTrait;
}
CODE_SAMPLE
, ['SomeTrait' => 'SomeInterface'])]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
throw new ShouldNotHappenException(sprintf('"%s" rule is deprecated, as too niche; use a custom rule scoped to your own trait and interface pair instead', self::class));
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
}
}
View on GitHub (pinned to 408fcb0ff1)
Solutions
- Remove AddInterfaceByTraitRector from rector.php.
- Add the interface to the relevant classes manually (usually a one-off, small set of classes).
- If the pairing is a standing convention in your codebase, write a tiny custom rector rule scoped to that exact trait/interface pair.
Example fix
// before (rector.php)
->withConfiguredRule(AddInterfaceByTraitRector::class, [
SomeTrait::class => SomeInterface::class,
])
// after (rector.php) - removed
// before
class Service
{
use SomeTrait;
}
// after
class Service implements SomeInterface
{
use SomeTrait;
} Defensive patterns
Strategy: validation
Validate before calling
use Rector\Transform\Rector\Class_\AddInterfaceByTraitRector;
$rules = [/* your list */ AddInterfaceByTraitRector::class];
if (in_array(AddInterfaceByTraitRector::class, $rules, true)) {
throw new InvalidArgumentException('Rule deprecated; add the interface manually or via a custom scoped rule');
} Try / catch
try {
exit($rectorApplication->run());
} catch (\Rector\Exception\ShouldNotHappenException $e) {
if (str_contains($e->getMessage(), 'AddInterfaceByTraitRector')) {
fwrite(STDERR, 'Remove the rule; the old trait=>interface configuration is ignored.' . PHP_EOL);
exit(1);
}
throw $e;
} Prevention
- Encode project-specific trait/interface conventions in your own custom rule inside the project, not via generic Transform rules.
- Notice the empty configure() body: configuration for this rule is silently discarded, so stale mappings hide in configs unnoticed.
- Clean rector.php of Transform rules that no longer have working implementations after an upgrade.
When it happens
Trigger: The rule is still referenced in rector.php (withRules() or withConfiguredRule() with the old trait=>interface map) and rector visits any class; refactor() throws immediately on the first Class_ node.
Common situations: Legacy configs from projects that bound e.g. a Laravel trait to a custom interface; rector upgrade breaking CI at the first class; configurations where the mapping was already ignored for a while without anyone noticing.
Related errors
- "%s" rule is deprecated, as a function call can rarely be tu
- "%s" rule is deprecated, as the scalar value target is too w
- "%s" rule is deprecated, as turning a docblock type into a r
- "%s" rule is deprecated, as risky. The "??" and "?:" operato
- "%s" is deprecated as depends on context and personal prefer
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/c5fd94027a85a3a0.
Report an issue: GitHub.