{"record":{"id":"0341aaf0fad18848","repo":"remix-run/react-router","slug":"a-history-only-accepts-one-active-listener","errorCode":null,"errorMessage":"A history only accepts one active listener","messagePattern":"A history only accepts one active listener","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/router/history.ts","lineNumber":749,"sourceCode":"    if (v5Compat && listener) {\n      listener({ action, location: history.location, delta: 0 });\n    }\n  }\n\n  function createURL(to: To): URL {\n    return createBrowserURLImpl(window, to);\n  }\n\n  let history: History = {\n    get action() {\n      return action;\n    },\n    get location() {\n      return getLocation(window, globalHistory);\n    },\n    listen(fn: Listener) {\n      if (listener) {\n        throw new Error(\"A history only accepts one active listener\");\n      }\n      window.addEventListener(PopStateEventType, handlePop);\n      listener = fn;\n\n      return () => {\n        window.removeEventListener(PopStateEventType, handlePop);\n        listener = null;\n      };\n    },\n    createHref(to) {\n      return createHref(window, to);\n    },\n    createURL,\n    encodeLocation(to) {\n      // Encode a Location the same way window.location would\n      let url = createURL(to);\n      return {\n        pathname: url.pathname,","sourceCodeStart":731,"sourceCodeEnd":767,"githubUrl":"https://github.com/remix-run/react-router/blob/917e6fdc0f8beb141e3380e83e27634d7be84e1d/packages/react-router/lib/router/history.ts#L731-L767","documentation":"A history instance supports exactly one active listener, and that slot is taken by the router itself (it subscribes via listen() to drive state updates). lib/router/history.ts:749 throws when listen() is called again before the previous listener unsubscribes, surfacing that two pieces of code think they own the same history.","triggerScenarios":"Passing the same createBrowserHistory() instance to two routers (two <RouterProvider>s, or a router plus a custom listen call); calling history.listen(...) yourself on the history already given to createBrowserRouter/createRouter; listening again in an effect whose cleanup never ran.","commonSituations":"Sharing a module-level history singleton between a main app and a modal/secondary router; sync/DevTools code that calls history.listen for URL tracking; test setups that mount the app twice without unmounting.","solutions":["Create a separate history instance for each router","If you need to observe the router, use router.subscribe() or router.navigate listeners instead of history.listen()","Ensure any manual listen() subscription is cleaned up (call the returned unsubscribe) before listening again","Check that only one <RouterProvider> consumes a given history"],"exampleFix":"// before (shared instance, second listen throws)\nconst history = createBrowserHistory()\nconst routerA = createBrowserRouter(routes, { history })\nhistory.listen(trackUrls)\n// after\nconst routerA = createBrowserRouter(routes)\nrouterA.subscribe(trackUrls)","handlingStrategy":"validation","validationCode":"// Before creating a second router on a shared history, verify it is unlistened\nconst history = createBrowserHistory();\n// one router per history instance:\nconst router = createBrowserRouter(routes, { history });","typeGuard":null,"tryCatchPattern":"try { history.listen(fn) } catch (e) { if (e instanceof Error && e.message.includes('one active listener')) { /* unsubscribe previous, then retry */ prevUnsubscribe?.(); history.listen(fn) } else throw e }","preventionTips":["Never share one history instance across routers or components","Prefer router.subscribe() to observe navigation instead of history.listen()","Always store and call the unsubscribe function returned by listen()"],"tags":["history","listener","router","lifecycle"],"backgroundTag":"duplicate-event-listener","analyzedSha":"917e6fdc0f8beb141e3380e83e27634d7be84e1d","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}