{"record":{"id":"7c46cd1fadf8ecec","repo":"reduxjs/redux","slug":"you-may-not-call-store-subscribe-while-the-reduc","errorCode":null,"errorMessage":"You may not call store.subscribe() while the reducer is executing. 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.","messagePattern":"You may not call store\\.subscribe\\(\\) while the reducer is executing\\. 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\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/createStore.ts","lineNumber":211,"sourceCode":"   * might have been updated multiple times during a nested `dispatch()` before\n   * the listener is called. It is, however, guaranteed that all subscribers\n   * registered before the `dispatch()` started will be called with the latest\n   * state by the time it exits.\n   *\n   * @param listener A callback to be invoked on every dispatch.\n   * @returns A function to remove this change listener.\n   */\n  function subscribe(listener: () => void) {\n    if (typeof listener !== 'function') {\n      throw new Error(\n        `Expected the listener to be a function. Instead, received: '${kindOf(\n          listener\n        )}'`\n      )\n    }\n\n    if (isDispatching) {\n      throw new Error(\n        'You may not call store.subscribe() while the reducer is executing. ' +\n          'If you would like to be notified after the store has been updated, subscribe from a ' +\n          'component and invoke store.getState() in the callback to access the latest state. ' +\n          'See https://redux.js.org/api/store#subscribelistener for more details.'\n      )\n    }\n\n    let isSubscribed = true\n\n    ensureCanMutateNextListeners()\n    const listenerId = listenerIdCounter++\n    nextListeners.set(listenerId, listener)\n\n    return function unsubscribe() {\n      if (!isSubscribed) {\n        return\n      }\n","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/reduxjs/redux/blob/3084fc33bb23ab4cab4796172eafe342da4e73d9/src/createStore.ts#L193-L229","documentation":"Subscribing during a dispatch would mutate the listener set mid-iteration and could let the new listener fire for an action it never asked about, or skip listeners entirely. Redux uses the isDispatching guard on subscribe() so new subscriptions only take effect from the next dispatch onward, and the snapshotted listener set stays stable for the current one.","triggerScenarios":"A reducer that calls store.subscribe(); a subscriber callback that subscribes a second listener while iterating; middleware that calls api.subscribe() during next(action); code that subscribes inside an existing listener without deferring.","commonSituations":"Hot-reload or code-splitting logic that wires up new subscriptions during an action; component-lifecycle code invoked synchronously during dispatch; subscriber that conditionally registers more listeners based on the action.","solutions":["Move the subscribe call out of the reducer/dispatch path; subscribe at module load or component-mount time, before any dispatch.","If a listener needs to register another listener, defer with queueMicrotask/setTimeout so the new subscription lands after the current dispatch.","Restructure so conditional subscriptions are decided up front rather than mid-dispatch."],"exampleFix":"// before\nstore.subscribe(() => {\n  if (firstRun) store.subscribe(extraListener)  // throws: dispatch in progress\n})\n\n// after\nstore.subscribe(() => {\n  if (firstRun) queueMicrotask(() => store.subscribe(extraListener))\n})","handlingStrategy":"validation","validationCode":"// Schedule any subscribe call that might run during dispatch:\nconst safeSubscribe = (store, listener) => {\n  if (typeof queueMicrotask === 'function') {\n    queueMicrotask(() => store.subscribe(listener))\n  } else {\n    setTimeout(() => store.subscribe(listener), 0)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Subscribe at module/component-mount time, never inside a reducer or dispatch path.","Defer mid-dispatch subscriptions with a microtask.","Keep subscribe/unsubscribe out of the listener callback body."],"tags":["redux","createstore","subscribe","isdispatching","listener-snapshot"],"backgroundTag":null,"analyzedSha":"3084fc33bb23ab4cab4796172eafe342da4e73d9","analyzedAt":"2026-08-12T13:21:41.727Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}