{"record":{"id":"bb1c74588db45cbe","repo":"remix-run/remix","slug":"hmr-must-be-a-function","errorCode":null,"errorMessage":"hmr must be a function","messagePattern":"hmr must be a function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/asset-server.ts","lineNumber":1084,"sourceCode":"      {\n        mounts,\n        rootDir,\n      },\n      ...getInjectedPackageMountConfigs(),\n    ]),\n    sourceMapSourcePaths: options.sourceMapSourcePaths ?? 'url',\n    sourceMaps: options.sourceMaps,\n    scriptsTarget: resolveScriptTarget(options.target),\n    stylesTarget: resolveStyleTarget(options.target),\n    watchOptions,\n  }\n}\n\nfunction normalizeHmrFactory(factory: AssetServerOptions['hmr']): BrowserHmrChannelFactory | null {\n  if (factory === undefined) return null\n\n  if (typeof factory !== 'function') {\n    throw new TypeError('hmr must be a function')\n  }\n\n  return factory\n}\n\nfunction createBrowserHmrChannel(\n  createChannel: BrowserHmrChannelFactory | null,\n  handleFileEvents: BrowserHmrFileEventHandler,\n  isClosed: () => boolean,\n  setUnsubscribe: (unsubscribe: () => void) => void,\n): Promise<BrowserHmrChannel | null> {\n  if (!createChannel) return Promise.resolve(null)\n\n  let channelOrPromise: BrowserHmrChannel | undefined | Promise<BrowserHmrChannel | undefined>\n  try {\n    channelOrPromise = createChannel()\n  } catch (error) {\n    return Promise.reject(error)","sourceCodeStart":1066,"sourceCodeEnd":1102,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/asset-server.ts#L1066-L1102","documentation":"Some `remix test` options require a positive integer. `optionalPositiveInteger` first runs the number check, then rejects values that aren't integers or are below 1 — zero, negatives, decimals, and Infinity all fail `Number.isInteger(n) && n >= 1`.","triggerScenarios":"Passing `0`, `-1`, `2.5`, or empty string to a positive-integer option of `remix test` (e.g. retries or worker counts).","commonSituations":"CI configs computing counts that land on 0; division in scripts producing decimals; empty env vars expanding into the flag.","solutions":["Use a value of 1 or greater: `--retries 2`","Default computed zero/empty values to omitting the flag entirely","Validate in scripts: only pass the flag when the value is a positive integer"],"exampleFix":"# before\nremix test --retries 0\n# after\nremix test --retries 1","handlingStrategy":"validation","validationCode":"function positiveIntOrOmit(raw: string | undefined): string[] {\n  let n = raw === undefined ? NaN : Number(raw);\n  return Number.isInteger(n) && n >= 1 ? [String(n)] : [];\n}\nrun(['remix','test','--retries', ...positiveIntOrOmit(process.env.RETRIES)])","typeGuard":"function isPositiveIntegerFlag(v: string): boolean {\n  let n = Number(v);\n  return v.trim() !== '' && Number.isInteger(n) && n >= 1;\n}","tryCatchPattern":null,"preventionTips":["Clamp computed counts to >= 1 or omit the flag","Never pass 0 to retry/worker-style options"],"tags":["cli","test","option-validation"],"backgroundTag":"invalid-option-value","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}