rectorphp/rector · error · ShouldNotHappenException
"%s" rule is deprecated, as it can cause BC breaks. The thro
Error message
"%s" rule is deprecated, as it can cause BC breaks. The thrown JsonException has to be handled manually on every call site
What it means
Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector is deprecated: instead of removing the class, its refactor() now unconditionally throws Rector\Exception\ShouldNotHappenException with the deprecation reason. The rule used to add JSON_THROW_ON_ERROR to json_encode()/json_decode() calls, but the resulting JsonException is not caught by code written for the old null/false error style, which is a behavioral (BC) break. Because getNodeTypes() returns NodeGroup::STMTS_AWARE, the exception fires on the first analyzed file containing statements.
Source
Thrown at rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php:41
json_encode($content);
json_decode($json);
CODE_SAMPLE
, <<<'CODE_SAMPLE'
json_encode($content, JSON_THROW_ON_ERROR);
json_decode($json, null, 512, JSON_THROW_ON_ERROR);
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return NodeGroup::STMTS_AWARE;
}
public function refactor(Node $node): ?Node
{
throw new ShouldNotHappenException(sprintf('"%s" rule is deprecated, as it can cause BC breaks. The thrown JsonException has to be handled manually on every call site', self::class));
}
public function provideMinPhpVersion(): int
{
return PhpVersionFeature::JSON_EXCEPTION;
}
}
View on GitHub (pinned to 408fcb0ff1)
Solutions
- Remove JsonThrowOnErrorRector from the ->withRules()/->withConfiguredRule() list in rector.php; the rule performs no migration anymore.
- If you want throwing JSON calls, edit each call site manually: pass JSON_THROW_ON_ERROR and wrap the call in a try/catch (JsonException) that fits the surrounding error handling.
- If automation is required for a controlled codebase, write a small project-scoped custom rule that adds the flag only where the call site was reviewed.
- As a temporary stopgap only, pin the last rector/rector version that ships the working rule; plan its removal before the next upgrade.
Example fix
// before (rector.php)
return RectorConfig::configure()
->withRules([JsonThrowOnErrorRector::class]);
// after (rector.php) - rule removed, migrate call sites by hand
return RectorConfig::configure()
->withPhp73();
// before (src/Api.php) // after (src/Api.php)
$value = json_decode($raw); try {
$value = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new MalformedPayloadException($raw, $e);
} Defensive patterns
Strategy: validation
Validate before calling
use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector;
$rules = [/* your withRules() list */ JsonThrowOnErrorRector::class];
$deprecated = [JsonThrowOnErrorRector::class];
$hit = array_intersect($rules, $deprecated);
if ($hit !== []) {
throw new InvalidArgumentException('Remove deprecated rector rules before running: ' . implode(', ', $hit));
} Try / catch
try {
exit($rectorApplication->run());
} catch (\Rector\Exception\ShouldNotHappenException $e) {
if (str_contains($e->getMessage(), 'rule is deprecated')) {
// config debt, not a code problem: strip the named rule from rector.php and re-run
fwrite(STDERR, 'Config fix needed: ' . $e->getMessage() . PHP_EOL);
exit(1);
}
throw $e;
} Prevention
- After every rector/rector upgrade, grep rector.php for rule names listed in the release notes' removed-rules section and delete them.
- Prefer version sets (->withPhp73()) over long explicit withRules() lists; sets are maintained by rector and drop gutted rules.
- Run vendor/bin/rector --dry-run on a small fixture path in CI before processing the whole codebase.
- Treat a ShouldNotHappenException containing 'rule is deprecated' as a config problem - never patch the analyzed code around it.
When it happens
Trigger: The rule is still listed in rector.php (via ->withRules([JsonThrowOnErrorRector::class]) or ->withConfiguredRule(...)) and `vendor/bin/rector` visits any statement-aware node; refactor() is called on the first match and throws immediately.
Common situations: Upgrading rector/rector to a release where this rule was gutted while keeping an old rector.php; CI failing on the first parsed file after `composer update`; configs shared across projects that still reference the rule name.
Related errors
- "%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
- "%s" rule is deprecated, as it is a personal preference that
- "%s" is deprecated, as simplifying regex ranges is a persona
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/4b1708147e6e6422.
Report an issue: GitHub.