remix-run/react-router · error
A history only accepts one active listener
Error message
A history only accepts one active listener
What it means
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.
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
Example fix
// before (shared instance, second listen throws)
const history = createBrowserHistory()
const routerA = createBrowserRouter(routes, { history })
history.listen(trackUrls)
// after
const routerA = createBrowserRouter(routes)
routerA.subscribe(trackUrls) Defensive patterns
Strategy: validation
Validate before calling
// Before creating a second router on a shared history, verify it is unlistened
const history = createBrowserHistory();
// one router per history instance:
const router = createBrowserRouter(routes, { history }); Try / catch
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 } Prevention
- 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()
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- You cannot use navigator.back() on the server because it is…
- You cannot use navigator.forward() on the server because it…
- You cannot use navigator.go() on the server because it is a…
- You cannot use router.
AI-assisted analysis of remix-run/react-router@917e6fdc0f (2026-08-18).
Data as JSON: /api/errors/0341aaf0fad18848.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-router/lib/router/history.ts:749
if (v5Compat && listener) {
listener({ action, location: history.location, delta: 0 });
}
}
function createURL(to: To): URL {
return createBrowserURLImpl(window, to);
}
let history: History = {
get action() {
return action;
},
get location() {
return getLocation(window, globalHistory);
},
listen(fn: Listener) {
if (listener) {
throw new Error("A history only accepts one active listener");
}
window.addEventListener(PopStateEventType, handlePop);
listener = fn;
return () => {
window.removeEventListener(PopStateEventType, handlePop);
listener = null;
};
},
createHref(to) {
return createHref(window, to);
},
createURL,
encodeLocation(to) {
// Encode a Location the same way window.location would
let url = createURL(to);
return {
pathname: url.pathname,View on GitHub (pinned to 917e6fdc0f)