remix-run/remix · error · TypeError
hmr must create a channel with an updateWatchedFiles functio
Error message
hmr must create a channel with an updateWatchedFiles function
What it means
The HMR channel contract requires `updateWatchedFiles(files)` so the asset server can tell the channel which files it depends on (e.g. lazy compilation dependencies). This error fires when the object returned by `hmr.createChannel` lacks a callable `updateWatchedFiles`.
Source
Thrown at packages/assets/src/lib/asset-server.ts:1133
return channel
})
}
function validateBrowserHmrChannel(channel: unknown): asserts channel is BrowserHmrChannel {
if (channel === null || typeof channel !== 'object') {
throw new TypeError('hmr must create an object')
}
if (!('url' in channel) || typeof channel.url !== 'string') {
throw new TypeError('hmr must create a channel with a string url')
}
if (!('close' in channel) || typeof channel.close !== 'function') {
throw new TypeError('hmr must create a channel with a close function')
}
if (!('onFileEvents' in channel) || typeof channel.onFileEvents !== 'function') {
throw new TypeError('hmr must create a channel with an onFileEvents function')
}
if (!('updateWatchedFiles' in channel) || typeof channel.updateWatchedFiles !== 'function') {
throw new TypeError('hmr must create a channel with an updateWatchedFiles function')
}
}
function normalizeBasePath(basePath: string): string {
if (typeof basePath !== 'string') {
throw new TypeError('basePath must be a string')
}
return normalizePathname(basePath || '/').replace(/\/+$/, '') || '/'
}
function normalizeFingerprintOptions(options: {
fingerprint: AssetServerOptions['fingerprint']
watch: AssetServerOptions['watch']
}):
| {
enabled: false
buildId?: stringView on GitHub (pinned to 9696913134)
Solutions
- Add `updateWatchedFiles(files: string[]): void` to the channel that forwards the list to the underlying watcher (add/remove watches as needed)
- Ensure `url`, `close`, and `onFileEvents` are also present
- Use the default channel by omitting `hmr.createChannel`
Example fix
// before
createChannel: () => ({ url, close, onFileEvents })
// after
createChannel: () => ({
url,
close,
onFileEvents,
updateWatchedFiles(files) {
watcher.add(files)
},
}) Defensive patterns
Strategy: validation
Validate before calling
let channel = createChannel()
if (typeof channel.updateWatchedFiles !== 'function') throw new Error('channel must implement updateWatchedFiles') Type guard
function hasUpdateWatchedFiles(c: unknown): c is { updateWatchedFiles: (files: string[]) => void } {
return !!c && typeof (c as any).updateWatchedFiles === 'function'
} Prevention
- Re-use the same channel factory across dev setups so updateWatchedFiles is never forgotten
- Assert the full interface in a smoke test before starting the server
When it happens
Trigger: `createChannel` returns a channel missing the `updateWatchedFiles` function, so the server cannot sync its watched-file set with the channel.
Common situations: Custom channels built against an earlier channel interface; minimal mock channels in plugins or tests that omit the watch-sync API.
Related errors
- hmr must create a channel with an onFileEvents function
- hmr must create a channel with a close function
- Browser HMR is disabled for this node-hmr runtime
- basePath must be a string
- fingerprint.buildId must be a string
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/af34ad1777c0870f.
Report an issue: GitHub.