facebook / react · Issue No. 37138
react-compiler-healthcheck@1.0.0 reports a perfect score on every codebase, because it can never record a failure. isActionableDiagnostic switches on ErrorSeverity members that no longer exist in the enum the same bundle ships, so every CompileError falls through to default: and throws — inside a logger whose caller swallows it.
Both halves are in healthcheck's own dist, so this is not a version-mismatch between packages:
// dist, ~line 18376 — the enum it bundles
ErrorSeverity2["Error"] = "Error";
ErrorSeverity2["Warning"] = "Warning";
ErrorSeverity2["Hint"] = "Hint";
ErrorSeverity2["Off"] = "Off";
// dist, ~line 54314 — the switch, still on the pre-1.0 member names
function isActionableDiagnostic(detail) {
switch (detail.severity) {
case ErrorSeverity.InvalidReact: // undefined
case ErrorSeverity.InvalidJS: // undefined
return true;
case ErrorSeverity.InvalidConfig: // undefined
case ErrorSeverity.Invariant: // undefined
case ErrorSeverity.CannotPreserveMemoization: // undefined
case ErrorSeverity.Todo: // undefined
return false;
default:
throw new Error(`Unhandled error severity \`${detail.severity}\``);
}
}
All six case labels evaluate to undefined. A real detail.severity of "Error" matches none of them, hits default, and throws Unhandled error severity \Error`. The throw happens inside logger.logEvent, and compile()wraps the run intry { … } catch {}— so the exception is swallowed,ActionableFailures/OtherFailuresstay empty,totalComponents === successes`, and the summary can only ever print "N out of N". It also aborts that file early, so successes after the first failure in the same file are lost too.
Any project with at least one bail-out. In a monorepo app I measured independently:
$ bunx react-compiler-healthcheck@1.0.0 --src "apps/monitoring/src/**/*.{ts,tsx}"
Successfully compiled 24 out of 24 components.
Running @babel/core + babel-plugin-react-compiler@1.0.0 directly over the same glob with a capturing logger — same options healthcheck uses (noEmit, compilationMode: 'infer', panicThreshold: 'critical_errors') — gives 24 compiled, 6 bailed (80%). Same 24 successes; the 6 failures are simply never recorded.
Across 651 files the direct run reports 926 compiled / 73 bailed (92.7%); healthcheck reports 100% for every app.
The failure is silent and biased in the dangerous direction. A team runs healthcheck, sees 100%, and concludes there is nothing to investigate — the exact opposite of the truth, and with no error output to suggest otherwise. It also makes healthcheck useless as a before/after signal for an adoption rollout, which is its main use.
Map the switch onto the current enum (Error / Warning / Hint / Off), and make the default non-fatal — an unrecognised severity should count the diagnostic and warn, never throw. Given the throw is swallowed one frame up, a catch that at minimum logged would have surfaced this immediately; that seems worth doing regardless.
Version: react-compiler-healthcheck@1.0.0, babel-plugin-react-compiler@1.0.0, React 19.2.5, Node 24.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.