remix-run/remix · error · TypeError

hmr must create a channel with a string url

Error message

hmr must create a channel with a string url

What it means

`remix version` prints `context.remixVersion`, which is injected into the CLI context at build/publish time. When it's null/undefined — typically a dev checkout or a broken/incomplete packaging — the command can't print a meaningful version and throws `RMX_REMIX_VERSION_UNAVAILABLE`, with the surrounding catch writing the error to stderr.

Source

Thrown at packages/assets/src/lib/asset-server.ts:1124

    if (channel === undefined) return null
    validateBrowserHmrChannel(channel)

    if (isClosed()) {
      channel.close()
      return null
    }

    setUnsubscribe(channel.onFileEvents(handleFileEvents))
    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(/\/+$/, '') || '/'

View on GitHub (pinned to 9696913134)

Solutions

  1. Use a published CLI from npm: `npx remix version`
  2. If developing the CLI, run the build/version-injection step so `remixVersion` is populated
  3. Reinstall the CLI package in case the install is corrupted
Defensive patterns

Strategy: fallback

Validate before calling

// Read the version from the installed package instead of the CLI context
let version = JSON.parse(fs.readFileSync('node_modules/remix/package.json','utf8')).version;
console.log(version);

Type guard

function hasVersion(ctx: { remixVersion: string | null }): boolean {
  return ctx.remixVersion != null;
}

Try / catch

Catch around `remix version`; on failure fall back to reading node_modules/remix/package.json for the version string.

Prevention

When it happens

Trigger: Invoking `remix version` when the running CLI's context lacks `remixVersion` (null or undefined), e.g. running from source without the version-injection build step or a corrupted install.

Common situations: Running the CLI from a repo checkout; packaging pipelines that skip version injection; corrupted node_modules installs.

Related errors


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