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

  1. Coerce env strings with Number() and validate with Number.isInteger before passing
  2. Use a real port in 0-65535, or 0 to let the OS assign one
  3. 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

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


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/12cf7cc8d3fb0ba8. Report an issue: GitHub.