facebook/react · error
Could not find helpers for a root. This is a bug in React Re
Error message
Could not find helpers for a root. This is a bug in React Refresh.
What it means
During performReactRefresh(), the runtime snapshots failedRoots and helpersByRoot and requires every failed root to have helper functions (scheduleRefresh/scheduleRoot) registered by the commit hook that injectIntoGlobalHook() installed (helpersByRoot.set in ReactFreshRuntime.js:535). This iteration over failedRoots throws when a root is tracked without registered helpers. The message says 'This is a bug in React Refresh': it indicates duplicated runtimes or version skew inside the refresh machinery, not application misuse.
Source
Thrown at packages/react-refresh/src/ReactFreshRuntime.js:271
// This ensures that if *new* roots are mounted, they'll use the resolve handler.
helpers.setRefreshHandler(resolveFamily);
});
let didError = false;
let firstError = null;
// We snapshot maps and sets that are mutated during commits.
// If we don't do this, there is a risk they will be mutated while
// we iterate over them. For example, trying to recover a failed root
// may cause another root to be added to the failed list -- an infinite loop.
const failedRootsSnapshot = cloneSet(failedRoots);
const mountedRootsSnapshot = cloneSet(mountedRoots);
const helpersByRootSnapshot = cloneMap(helpersByRoot);
failedRootsSnapshot.forEach(root => {
const helpers = helpersByRootSnapshot.get(root);
if (helpers === undefined) {
throw new Error(
'Could not find helpers for a root. This is a bug in React Refresh.',
);
}
if (!failedRoots.has(root)) {
// No longer failed.
}
if (rootElements === null) {
return;
}
if (!rootElements.has(root)) {
return;
}
const element = rootElements.get(root);
try {
helpers.scheduleRoot(root, element);
} catch (err) {
if (!didError) {
didError = true;View on GitHub (pinned to eafeac097b)
Solutions
- Dedupe react, react-dom and react-refresh to a single version each (yarn resolutions, npm overrides, or bundler aliases)
- Ensure injectIntoGlobalHook is called exactly once per JS context by one runtime copy
- Align the react-refresh major with the react-dom version in use and reinstall
- If versions are aligned and it still throws, capture a minimal repro and report it to facebook/react — the message explicitly marks it as a React bug
Example fix
// before: two copies resolve
// package.json (npm)
{
"overrides": {
"react-refresh": "0.14.2"
}
}
// after: verify single copy after reinstall
// $ npm ls react-refresh -> exactly one version, hoisted at root Defensive patterns
Strategy: validation
Validate before calling
// Verify only one react-refresh copy is resolvable before enabling HMR
const paths = require.resolve.paths('react-refresh');
const copies = require('child_process')
.execSync('npm ls react-refresh --all || true')
.toString();
if ((copies.match(/react-refresh@/g) || []).length > 1) {
throw new Error('Duplicate react-refresh detected - dedupe before enabling Fast Refresh');
} Try / catch
try {
performReactRefresh();
} catch (e) {
if (e instanceof Error && e.message.includes('bug in React Refresh')) {
// internal state corruption from duplicate/skewed runtimes: log and disable hot refresh
} else throw e;
} Prevention
- Pin single versions of react, react-dom and react-refresh via overrides/resolutions
- Call injectIntoGlobalHook exactly once per JS context
- Run npm ls react-refresh (or yarn why) in CI to catch duplicate copies early
When it happens
Trigger: Two copies of react-refresh in one JS context — one injected into the global hook, another executing performReactRefresh; roots committed by a renderer whose helpers were registered by a different react/react-dom version (one runtime fills mountedRoots, a mismatched runtime iterates helpersByRoot); calling injectIntoGlobalHook twice from different runtime copies.
Common situations: Monorepos where react-dom and a second renderer (react-native-web, react-three-fiber, ink) each pull their own react-refresh; partial upgrades leaving react, react-dom and react-refresh on mismatched versions; bundler dedupe disabled so two react-refresh instances resolve.
Related errors
- 321
- React Refresh Babel transform should only be enabled in deve
- React Refresh runtime should not be included in the producti
- 525
- react-cache: read and preload may only be called from within
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/2c962ecab7461364.
Report an issue: GitHub.