reduxjs/redux · error · Error

The slice reducer for key "${key}" returned undefined when p

Error message

The slice reducer for key "${key}" returned undefined when probed with a random type. Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined, but can be null.

What it means

assertReducerShape also probes each reducer with a random unknown action type (ActionTypes.PROBE_UNKNOWN_ACTION). For any action it does not recognize, the reducer must return the current state (or initial state when current is undefined). Returning undefined for an unknown action signals the reducer swallows unknown actions instead of passing them through, which breaks reducer composition and time-travel.

Source

Thrown at src/combineReducers.ts:84

    const reducer = reducers[key]
    const initialState = reducer(undefined, { type: ActionTypes.INIT })

    if (typeof initialState === 'undefined') {
      throw new Error(
        `The slice reducer for key "${key}" returned undefined during initialization. ` +
          `If the state passed to the reducer is undefined, you must ` +
          `explicitly return the initial state. The initial state may ` +
          `not be undefined. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      )
    }

    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      throw new Error(
        `The slice reducer for key "${key}" returned undefined when probed with a random type. ` +
          `Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" ` +
          `namespace. They are considered private. Instead, you must return the ` +
          `current state for any unknown actions, unless it is undefined, ` +
          `in which case you must return the initial state, regardless of the ` +
          `action type. The initial state may not be undefined, but can be null.`
      )
    }
  })
}

/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @template S Combined state object type.

View on GitHub (pinned to 3084fc33bb)

Solutions

  1. End every reducer's switch with `default: return state` so unknown actions pass through unchanged.
  2. If you use if/else chains, add a final `return state` outside all conditions.
  3. Confirm the reducer never returns undefined for INIT (error 2 fires first if it does) — fix the initial-state default if both errors appear.

Example fix

// before
function counter(state = 0, action) {
  switch (action.type) {
    case 'INC': return state + 1
  }
}

// after
function counter(state = 0, action) {
  switch (action.type) {
    case 'INC': return state + 1
    default: return state
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Probe unknown-action behavior of each slice:
const probeUnknown = slices => {
  for (const [key, reducer] of Object.entries(slices)) {
    const out = reducer(undefined, { type: '@@PROBE_' + Math.random() })
    if (typeof out === 'undefined') throw new Error(`Slice '${key}' returns undefined for unknown actions`)
  }
}
probeUnknown({ todos, filter })

Type guard

const handlesUnknownAction = reducer =>
  typeof reducer(undefined, { type: '@@RANDOM_' + Math.random() }) !== 'undefined'

Prevention

When it happens

Trigger: A reducer that returns undefined for unhandled action types because the switch has no `default: return state`; a reducer that explicitly handles only known actions and falls off the end of the function returning undefined; a reducer that pattern-matches on a closed list of types and forgets the fallback.

Common situations: Switch statement missing `default: return state`; reducer authored as `if (action.type === X) return newState` with no else; refactoring that drops the trailing return; TypeScript reducer where exhaustiveness checking tempts authors to omit a default branch.

Related errors


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