{"record":{"id":"6a87f8bc049ccf89","repo":"remix-run/react-router","slug":"onchange-is-not-supported-when-watch-mode-is-disab","errorCode":null,"errorMessage":"onChange is not supported when watch mode is disabled","messagePattern":"onChange is not supported when watch mode is disabled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/config/config.ts","lineNumber":870,"sourceCode":"    validateConfig,\n  });\n\n  if (!initialConfigResult.ok) {\n    throw new Error(initialConfigResult.error);\n  }\n\n  appDirectory = Path.normalize(initialConfigResult.value.appDirectory);\n\n  let currentConfig = initialConfigResult.value;\n\n  let fsWatcher: FSWatcher | undefined;\n  let changeHandlers: ChangeHandler[] = [];\n\n  return {\n    getConfig,\n    onChange: (handler: ChangeHandler) => {\n      if (!watch) {\n        throw new Error(\n          \"onChange is not supported when watch mode is disabled\",\n        );\n      }\n\n      changeHandlers.push(handler);\n\n      if (!fsWatcher) {\n        fsWatcher = chokidar.watch([root, appDirectory], {\n          ignoreInitial: true,\n          ignored: (path) => isIgnoredByWatcher(path, { root, appDirectory }),\n        });\n\n        fsWatcher.on(\"error\", (error: unknown) => {\n          let message = error instanceof Error ? error.message : String(error);\n          console.warn(colors.yellow(`File watcher error: ${message}`));\n        });\n\n        fsWatcher.on(\"all\", async (...args) => {","sourceCodeStart":852,"sourceCodeEnd":888,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/config/config.ts#L852-L888","documentation":"createConfigLoader() only sets up a chokidar file watcher when constructed with watch: true. Calling onChange() on a loader built with watch: false throws immediately because there is no watcher to subscribe handlers to — the guard sits at the top of onChange before any handler registration.","triggerScenarios":"Programmatic use: const loader = await createConfigLoader({ ..., watch: false }); loader.onChange(handler). Common when code written against the dev-server path (watch: true) is reused for one-shot build/load paths, or loadConfig-adjacent utilities are handed a non-watch loader.","commonSituations":"Custom build scripts or plugins calling @react-router/dev internals; refactors where onChange registration moved outside the watch-enabled branch.","solutions":["Pass watch: true to createConfigLoader if you need change events","Otherwise do not register onChange — call loader.getConfig() on demand instead","For one-shot reads use loadConfig(), which never exposes onChange"],"exampleFix":"// before\nconst loader = await createConfigLoader({ rootDirectory, mode, watch: false });\nloader.onChange(handler); // throws\n\n// after\nconst loader = await createConfigLoader({ rootDirectory, mode, watch: true });\nconst unsubscribe = loader.onChange(handler);","handlingStrategy":"validation","validationCode":"import { createConfigLoader } from \"@react-router/dev/config\";\n\nconst watch = process.env.NODE_ENV === \"development\";\nconst loader = await createConfigLoader({ rootDirectory, mode, watch });\n\n// Only subscribe when the loader was created with watch: true\nconst unsubscribe = watch ? loader.onChange(handler) : null;","typeGuard":"type WatchableConfigLoader = ReturnType<\n  typeof createConfigLoader<{ watch: true }>\n> & { onChange(handler: (evt: unknown) => void): () => void };\n\nfunction supportsOnChange(\n  loader: Awaited<ReturnType<typeof createConfigLoader>>,\n): loader is WatchableConfigLoader {\n  return \"onChange\" in loader;\n}","tryCatchPattern":null,"preventionTips":["Track the watch flag you passed and branch onChange registration on it","Use loadConfig() for one-shot reads — it never exposes onChange","Treat onChange availability as part of the loader contract, not a universal method"],"tags":["config","api-misuse","watch-mode"],"backgroundTag":"watch-mode-unavailable","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}