reduxjs/redux · error · Error

Expected the listener to be a function. Instead, received: '

Error message

Expected the listener to be a function. Instead, received: '${kindOf(listener)}'

What it means

store.subscribe(listener) requires listener to be a function; it is invoked on every dispatch. Passing anything else (undefined, object, string) means there is nothing to call back, so Redux rejects it immediately with a kindOf() report of what was actually passed.

Source

Thrown at src/createStore.ts:203

   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param listener A callback to be invoked on every dispatch.
   * @returns A function to remove this change listener.
   */
  function subscribe(listener: () => void) {
    if (typeof listener !== 'function') {
      throw new Error(
        `Expected the listener to be a function. Instead, received: '${kindOf(
          listener
        )}'`
      )
    }

    if (isDispatching) {
      throw new Error(
        '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.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()

View on GitHub (pinned to 3084fc33bb)

Solutions

  1. Pass a plain function: `store.subscribe(() => console.log(store.getState()))`.
  2. If you intended observer-pattern semantics, use the observable interop: `store[Symbol.observable]().subscribe({ next: state => ... })`.
  3. Bind extracted methods: `store.subscribe(myObj.onChange.bind(myObj))`.
  4. Default-check the argument before subscribing: `if (typeof cb === 'function') unsub = store.subscribe(cb)`.

Example fix

// before
store.subscribe({ next: () => render() })   // object, not function

// after
const unsub = store.subscribe(() => render())
// or use the observable interop for observer objects:
store[Symbol.observable]().subscribe({ next: state => render() })
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof listener !== 'function') {
  throw new TypeError(`listener must be a function, got ${typeof listener}`)
}
const unsub = store.subscribe(listener)

Type guard

const isListener = (x): x is () => void => typeof x === 'function'

Prevention

When it happens

Trigger: store.subscribe() with no argument; store.subscribe({ next: fn }) (passing an observer object to subscribe instead of the observable interop); store.subscribe(myObj.onChange) without binding `this` losing the function reference; subscribe(null) by accident.

Common situations: Confusing store.subscribe(listener) with store[Symbol.observable]().subscribe(observer) — the latter takes an object, the former a function; destructuring a method off an object without binding; passing a React component as the listener; legacy code passing an event-emitter.

Related errors


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