reduxjs/redux · error · Error
You may not unsubscribe from a store listener while the redu
Error message
You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api/store#subscribelistener for more details.
What it means
The unsubscribe function returned by subscribe() is also guarded by isDispatching. Removing a listener while the iteration is mid-flight could skip listeners that should have fired. Redux requires unsubscription to happen outside the dispatch window so the snapshot stays consistent for the whole dispatch.
Source
Thrown at src/createStore.ts:231
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
const listenerId = listenerIdCounter++
nextListeners.set(listenerId, listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
nextListeners.delete(listenerId)
currentListeners = null
}
}
/**
* Dispatches an action. It is the only way to trigger a state change.
*
* The `reducer` function, used to create the store, will be called with the
* current state tree and the given `action`. Its return value willView on GitHub (pinned to 3084fc33bb)
Solutions
- Defer self-unsubscription to after the dispatch: wrap the unsubscribe in queueMicrotask/setTimeout/Promise.resolve().then().
- Use the `isSubscribed` boolean guard the unsubscribe already provides (calling twice is a no-op), but schedule the first call outside dispatch.
- For single-fire subscriptions, set a flag in the callback and unsubscribe in a microtask.
Example fix
// before
const unsub = store.subscribe(() => {
doWork()
unsub() // throws: dispatch in progress
})
// after
const unsub = store.subscribe(() => {
doWork()
queueMicrotask(unsub)
}) Defensive patterns
Strategy: validation
Validate before calling
// Defer self-unsubscription out of the dispatch window:
const subscribeOnce = (store, listener) => {
let unsub
let done = false
unsub = store.subscribe(() => {
listener()
if (!done) { done = true; queueMicrotask(() => unsub()) }
})
return unsub
} Prevention
- Never call unsubscribe() synchronously inside its own listener during a dispatch.
- Defer unsubscription with queueMicrotask/setTimeout.
- For one-shot subscriptions, use a deferred unsubscribe pattern.
When it happens
Trigger: A listener that immediately unsubscribes itself synchronously (calling the returned unsubscribe inside its own callback while still inside the dispatch loop); a reducer that captures and calls an unsubscribe handle; middleware that unsubscribes during next(action).
Common situations: Single-fire subscription patterns (`const unsub = store.subscribe(() => { unsub(); ... })`) invoked during the dispatch; cleanup logic that runs during a dispatch path; race between React unmount and an in-flight dispatch.
Related errors
- You may not call store.subscribe() while the reducer is exec
- You may not call store.getState() while the reducer is execu
- Expected the listener to be a function. Instead, received: '
- Reducers may not dispatch actions.
- Expected the root reducer to be a function. Instead, receive
AI-assisted analysis of reduxjs/redux@3084fc33bb (2026-08-12).
Data as JSON: /api/errors/d8de90d06478e357.
Report an issue: GitHub.