remix-run/remix · error · Error
nodeHmrRuntimeUnavailableError
Error message
nodeHmrRuntimeUnavailableError
What it means
In the fallback runtime implementation (runtime.ts), throwNodeHmrRuntimeUnavailable throws Error('The node-hmr/runtime API is only available when running inside node-hmr') whenever createBrowserHmrChannel or emitServerReady is invoked outside a node-hmr-supervised process. Unlike the eager module-level throw, this runtime loads but every API call fails, telling you the host process lacks the node-hmr runtime global.
Source
Thrown at packages/node-hmr/src/runtime.ts:39
async function createBrowserHmrChannel() {
throwNodeHmrRuntimeUnavailable()
}
/**
* Notifies the `node-hmr` parent that this child process is ready to serve requests.
*
* Call this after the app server starts listening. After a restart, `node-hmr` waits for this
* signal before publishing the browser `server:update` event, preventing clients from refreshing
* against a server that is not ready yet.
*/
export const emitServerReady: NodeHmrRuntimeApi['emitServerReady'] = function emitServerReady() {
throwNodeHmrRuntimeUnavailable()
}
throwNodeHmrRuntimeUnavailable()
function throwNodeHmrRuntimeUnavailable(): never {
throw new Error(nodeHmrRuntimeUnavailableError)
}
View on GitHub (pinned to 9696913134)
Solutions
- Run the dev server through the node-hmr CLI so the runtime global exists
- Wrap createBrowserHmrChannel/emitServerReady calls in feature checks against the runtime global and skip them when absent
- Catch the error and degrade gracefully (skip browser HMR wiring / server-ready signaling)
Example fix
// before
runtime.emitServerReady()
// after
try {
runtime.emitServerReady()
} catch {
// not running under node-hmr; nothing to signal
} Defensive patterns
Strategy: try-catch
Validate before calling
if (globalThis.__nodeHmrRuntime !== undefined) {
runtime.createBrowserHmrChannel()
runtime.emitServerReady()
} Type guard
function hasNodeHmrRuntime(): boolean {
return typeof globalThis === 'object' && (globalThis as any).__nodeHmrRuntime !== undefined
} Try / catch
try {
runtime.emitServerReady()
} catch (error) {
if (error instanceof Error && error.message.includes('only available when running inside node-hmr')) {
// not under node-hmr — skip
} else throw error
} Prevention
- Check for the node-hmr runtime global before calling createBrowserHmrChannel/emitServerReady
- Run dev processes under the node-hmr supervisor
- Make server-ready signaling optional in scripts that also run standalone
When it happens
Trigger: Calling remixNodeHmrRuntime.createBrowserHmrChannel() or .emitServerReady() when getNodeHmrRuntime() returned undefined — i.e. any process not spawned/registered by node-hmr.
Common situations: Dev scripts bypassing the node-hmr CLI; unit tests exercising code that calls emitServerReady(); environments (CI) where the node-hmr supervisor global was never installed.
Related errors
- Browser HMR is disabled for this node-hmr runtime
- nodeHmrRuntimeUnavailableError
- hmr must create a channel with a close function
- hmr must create a channel with an onFileEvents function
- hmr must create a channel with an updateWatchedFiles functio
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/00ec2c39ab7e6e50.
Report an issue: GitHub.