remix-run/remix · error · TypeError

hmr must create an object

Error message

hmr must create an object

What it means

The `--pool` option selects the test execution pool and must be one of the supported `RemixTestPool` values (the exact list is included in the message). `parsePool` returns early for undefined or listed values and throws for anything else, so a typo'd or ported-from-Vitest pool name fails fast.

Source

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

  }

  return Promise.resolve(channelOrPromise).then((channel) => {
    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')

View on GitHub (pinned to 9696913134)

Solutions

  1. Use one of the pools listed in the error message, e.g. `--pool threads` or `--pool forks`
  2. Omit `--pool` to use the default
  3. Re-check supported pools after upgrading the CLI

Example fix

# before
remix test --pool node
# after
remix test --pool threads
Defensive patterns

Strategy: type-guard

Validate before calling

const SUPPORTED_POOLS = ['threads','forks'] as const;
let pool = process.env.TEST_POOL;
if (pool && !(SUPPORTED_POOLS as readonly string[]).includes(pool)) {
  console.error(`Pool must be one of: ${SUPPORTED_POOLS.join(', ')}`);
  process.exit(1);
}

Type guard

function isSupportedPool(v: string, pools: readonly string[]): v is string {
  return pools.includes(v);
}

Prevention

When it happens

Trigger: Passing `--pool node` or `--pool vmThreads` to `remix test` when the value isn't in the supported `pools` array for the installed version.

Common situations: Habits from Vitest/Jest where pool names differ; renamed pools across CLI versions; typos.

Related errors


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