reduxjs/redux · error · Error
Reducers may not dispatch actions.
Error message
Reducers may not dispatch actions.
What it means
Dispatching from inside a reducer (a re-entrant dispatch) is forbidden because reducers must be pure functions of (state, action) and must not cause side effects. The isDispatching flag is set true while currentReducer runs; if dispatch() is called again during that window, Redux throws to prevent infinite loops and corrupted state.
Source
Thrown at src/createStore.ts:294
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
if (typeof action.type !== 'string') {
throw new Error(
`Action "type" property must be a string. Instead, the actual type was: '${kindOf(
action.type
)}'. Value was: '${String(action.type)}' (stringified)`
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
listeners.forEach(listener => {
listener()
})
return action
}
/**
* Replaces the reducer currently used by the store to calculate the state.View on GitHub (pinned to 3084fc33bb)
Solutions
- Never dispatch from a reducer. Move chained logic to middleware (redux-thunk, redux-saga, redux-observable) where dispatching is allowed.
- If an action should trigger another, dispatch the follow-up from the action creator or a thunk, not from the reducer.
- For derived state, compute it with selectors (reselect) instead of dispatching a 'sync' action.
- In tests, dispatch follow-up actions sequentially outside the reducer call.
Example fix
// before
function reducer(state, action) {
if (action.type === 'LOGIN') {
store.dispatch({ type: 'FETCH_PROFILE' }) // throws: re-entrant dispatch
}
return state
}
// after (in a thunk, outside the reducer)
const login = creds => async (dispatch, getState) => {
await dispatch({ type: 'LOGIN', payload: creds })
dispatch({ type: 'FETCH_PROFILE' })
} Defensive patterns
Strategy: validation
Validate before calling
// Reducers must be pure: never dispatch inside them.
// Move chained dispatches to a thunk:
const chainedAction = () => async (dispatch, getState) => {
await dispatch(firstAction())
dispatch(secondAction()) // safe: outside reducer
} Prevention
- Never call store.dispatch() inside a reducer.
- Use middleware (redux-thunk/saga/observable) for action chaining.
- Compute derived state with selectors, not by dispatching sync actions.
- Lint reducers to flag any dispatch call.
When it happens
Trigger: A reducer that calls store.dispatch(...) to chain another action; middleware that dispatches inside the reducer phase; a thunk incorrectly invoked synchronously inside a reducer; selector/derived-state code that triggers a dispatch during reduction.
Common situations: Logic that 'auto-responds' to one action by dispatching another from within a reducer; porting event-driven code into reducers; subscriber that dispatches from a path that is itself a reducer callback; testing harness that dispatches inside a mock reducer.
Related errors
- You may not call store.getState() while the reducer is execu
- Expected the root reducer to be a function. Instead, receive
- You may not call store.subscribe() while the reducer is exec
- You may not unsubscribe from a store listener while the redu
- Actions must be plain objects. Instead, the actual type was:
AI-assisted analysis of reduxjs/redux@3084fc33bb (2026-08-12).
Data as JSON: /api/errors/6b14d3b8f5f027ff.
Report an issue: GitHub.