{"record":{"id":"b683dca4ff77b2e8","repo":"reduxjs/redux","slug":"actions-must-be-plain-objects-instead-the-actual","errorCode":null,"errorMessage":"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.","messagePattern":"Actions must be plain objects\\. Instead, the actual type was: '(.+?)'\\. 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\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/createStore.ts","lineNumber":272,"sourceCode":"   * dispatch a Promise, an Observable, a thunk, or something else, you need to\n   * wrap your store creating function into the corresponding middleware. For\n   * example, see the documentation for the `redux-thunk` package. Even the\n   * middleware will eventually dispatch plain object actions using this method.\n   *\n   * @param action A plain object representing “what changed”. It is\n   * a good idea to keep actions serializable so you can record and replay user\n   * sessions, or use the time travelling `redux-devtools`. An action must have\n   * a `type` property which may not be `undefined`. It is a good idea to use\n   * string constants for action types.\n   *\n   * @returns For convenience, the same action object you dispatched.\n   *\n   * Note that, if you use a custom middleware, it may wrap `dispatch()` to\n   * return something else (for example, a Promise you can await).\n   */\n  function dispatch(action: A) {\n    if (!isPlainObject(action)) {\n      throw new Error(\n        `Actions must be plain objects. Instead, the actual type was: '${kindOf(\n          action\n        )}'. 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.`\n      )\n    }\n\n    if (typeof action.type === 'undefined') {\n      throw new Error(\n        'Actions may not have an undefined \"type\" property. You may have misspelled an action type string constant.'\n      )\n    }\n\n    if (typeof action.type !== 'string') {\n      throw new Error(\n        `Action \"type\" property must be a string. Instead, the actual type was: '${kindOf(\n          action.type\n        )}'. Value was: '${String(action.type)}' (stringified)`\n      )","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/reduxjs/redux/blob/3084fc33bb23ab4cab4796172eafe342da4e73d9/src/createStore.ts#L254-L290","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Invoke action creators: `dispatch(addTodo(text))`, not `dispatch(addTodo)`.","Apply redux-thunk (or redux-saga, redux-observable) via applyMiddleware to handle functions/Promises/etc.","If batching, install a batching middleware (e.g. redux-batched-actions) rather than dispatching arrays.","Confirm class-instance actions are converted to plain objects ({...instance}) before dispatch."],"exampleFix":"// before\ndispatch(addTodo)                       // passing the function, not its result\nconst store = createStore(rootReducer)  // no thunk middleware\n\n// after\nimport { applyMiddleware, createStore } from 'redux'\nimport thunk from 'redux-thunk'\nconst store = createStore(rootReducer, applyMiddleware(thunk))\ndispatch(addTodo('buy milk'))           // plain object\ndispatch(fetchTodos())                  // thunk, handled by middleware","handlingStrategy":"type-guard","validationCode":"const isPlainAction = a =>\n  a !== null && typeof a === 'object' &&\n  (Object.getPrototypeOf(a) === null || Object.getPrototypeOf(a) === Object.prototype)\n\nif (!isPlainAction(action)) {\n  console.error('dispatch expects a plain object; got', action)\n} else {\n  store.dispatch(action)\n}","typeGuard":"const isPlainObjectAction = (x): x is { type: string; [k:string]: any } =>\n  x !== null && typeof x === 'object' &&\n  (Object.getPrototypeOf(x) === null || Object.getPrototypeOf(x) === Object.prototype)\n  && typeof x.type === 'string'","tryCatchPattern":null,"preventionTips":["Always invoke action creators: dispatch(addTodo(text)).","Install redux-thunk/saga before dispatching functions/Promises.","Convert class instances to plain objects before dispatch.","Use TypeScript so dispatch(action) only accepts plain Action-typed objects."],"tags":["redux","createstore","dispatch","action","plain-object","middleware"],"backgroundTag":null,"analyzedSha":"3084fc33bb23ab4cab4796172eafe342da4e73d9","analyzedAt":"2026-08-12T13:21:41.727Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}