reduxjs/redux · error · Error

Actions must be plain objects. Instead, the actual type was:

Error message

Actions must be plain objects. Instead, the actual type was: '${kindOf(action)}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.

What it means

dispatch() requires its argument to be a plain object — an action literal like { type, payload } produced by {} or Object.create(null). Functions, Promises, class instances, arrays, and primitive values are rejected because the core dispatch pipeline is synchronous and assumes a serializable action. To dispatch non-plain values you must add middleware (e.g. redux-thunk for functions).

Source

Thrown at src/createStore.ts:272

   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */
  function dispatch(action: A) {
    if (!isPlainObject(action)) {
      throw new Error(
        `Actions must be plain objects. Instead, the actual type was: '${kindOf(
          action
        )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
      )
    }

    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)`
      )

View on GitHub (pinned to 3084fc33bb)

Solutions

  1. Invoke action creators: `dispatch(addTodo(text))`, not `dispatch(addTodo)`.
  2. Apply redux-thunk (or redux-saga, redux-observable) via applyMiddleware to handle functions/Promises/etc.
  3. If batching, install a batching middleware (e.g. redux-batched-actions) rather than dispatching arrays.
  4. Confirm class-instance actions are converted to plain objects ({...instance}) before dispatch.

Example fix

// before
dispatch(addTodo)                       // passing the function, not its result
const store = createStore(rootReducer)  // no thunk middleware

// after
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(rootReducer, applyMiddleware(thunk))
dispatch(addTodo('buy milk'))           // plain object
dispatch(fetchTodos())                  // thunk, handled by middleware
Defensive patterns

Strategy: type-guard

Validate before calling

const isPlainAction = a =>
  a !== null && typeof a === 'object' &&
  (Object.getPrototypeOf(a) === null || Object.getPrototypeOf(a) === Object.prototype)

if (!isPlainAction(action)) {
  console.error('dispatch expects a plain object; got', action)
} else {
  store.dispatch(action)
}

Type guard

const isPlainObjectAction = (x): x is { type: string; [k:string]: any } =>
  x !== null && typeof x === 'object' &&
  (Object.getPrototypeOf(x) === null || Object.getPrototypeOf(x) === Object.prototype)
  && typeof x.type === 'string'

Prevention

When it happens

Trigger: dispatch(myActionCreator) where the creator was called but you forgot the parentheses; dispatch(() => ...) (a thunk) without redux-thunk applied; dispatch(Promise.resolve()); dispatch(new MyActionClass()); dispatch([action1, action2]) hoping for batch behavior.

Common situations: Forgot to call the action creator: `dispatch(addTodo)` instead of `dispatch(addTodo())`; thunks dispatched before applyMiddleware(thunk) is wired; passing a class instance action from a typed action factory; using Observable/Promise middleware that is not installed.

Related errors


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