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 will

View on GitHub (pinned to 3084fc33bb)

Solutions

  1. Defer self-unsubscription to after the dispatch: wrap the unsubscribe in queueMicrotask/setTimeout/Promise.resolve().then().
  2. Use the `isSubscribed` boolean guard the unsubscribe already provides (calling twice is a no-op), but schedule the first call outside dispatch.
  3. 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

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


AI-assisted analysis of reduxjs/redux@3084fc33bb (2026-08-12). Data as JSON: /api/errors/d8de90d06478e357. Report an issue: GitHub.