remix-run/remix · error · TypeError
Invalid browser HMR channel port: ${port}
Error message
Invalid browser HMR channel port: ${port} What it means
assertValidPort throws a TypeError when the browser HMR channel port is not an integer between 0 and 65535. It validates options passed to normalizeBrowserHmrChannelOptions in node-hmr before they are used to open the event stream, catching garbage config early.
Source
Thrown at packages/node-hmr/src/index.ts:242
if (options === false) return null
if (options === undefined || options === true) return {}
if (options.port !== undefined) {
assertValidPort(options.port)
}
return options
}
function resolveRegisterPath(): string {
let extension = import.meta.url.endsWith('.ts') ? 'ts' : 'js'
return fileURLToPath(new URL(`./register.${extension}`, import.meta.url))
}
function assertValidPort(port: number): void {
if (!Number.isInteger(port) || port < 0 || port > 65_535) {
throw new TypeError(`Invalid browser HMR channel port: ${port}`)
}
}
View on GitHub (pinned to 9696913134)
Solutions
- Coerce env strings with Number() and validate with Number.isInteger before passing
- Use a real port in 0-65535, or 0 to let the OS assign one
- Remove placeholder/sentinel values from config
Example fix
// before
createNodeHmrRuntime({ browserHmrChannel: { port: Number(process.env.HMR_PORT ?? '3000.5') } })
// after
let port = Number(process.env.HMR_PORT ?? 3000)
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error('Bad HMR_PORT')
createNodeHmrRuntime({ browserHmrChannel: { port } }) Defensive patterns
Strategy: validation
Validate before calling
function isValidPort(port: number): boolean {
return Number.isInteger(port) && port >= 0 && port <= 65535
}
let port = Number(config.hmrPort)
if (!isValidPort(port)) throw new Error(`Invalid HMR port: ${config.hmrPort}`) Type guard
function isValidPort(port: unknown): port is number {
return typeof port === 'number' && Number.isInteger(port) && port >= 0 && port <= 65535
} Try / catch
try {
normalizeBrowserHmrChannelOptions(opts)
} catch (error) {
if (error instanceof TypeError && /browser HMR channel port/.test(error.message)) {
// fix config and continue with a default port
} else throw error
} Prevention
- Convert port env vars with Number() and validate with Number.isInteger
- Never use sentinel values like -1 or 99999 for 'unset' — use undefined
When it happens
Trigger: Passing a fractional, negative, NaN, or >65535 port (e.g. from a string env var parsed incorrectly or a placeholder value) as the browser HMR channel port in node-hmr runtime options.
Common situations: Reading a port from an environment variable without Number() conversion or with parseFloat producing decimals; defaulting to -1 or 99999 as a sentinel; config typos.
Related errors
- basePath must be a string
- fingerprint.buildId must be a string
- fingerprint.buildId must be a non-empty string
- files.extensions must be an array
- files.extensions values must be strings
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/12cf7cc8d3fb0ba8.
Report an issue: GitHub.