reduxjs/redux · error · Error

Action "type" property must be a string. Instead, the actual

Error message

Action "type" property must be a string. Instead, the actual type was: '${kindOf(action.type)}'. Value was: '${String(action.type)}' (stringified)

What it means

After confirming type is defined, Redux also requires type to be a string. Symbols, numbers, objects, or booleans as type break devtools serialization, action recording, and string-based switch matching in reducers. The error includes kindOf and the stringified value to surface exactly what was passed.

Source

Thrown at src/createStore.ts:286

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

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)

View on GitHub (pinned to 3084fc33bb)

Solutions

  1. Use string constants for action types: `export const ADD_TODO = 'todos/add'`.
  2. If you want namespacing, prefix the string ('feature/ACTION') instead of using Symbols.
  3. Audit constant modules to confirm every exported type is `as const` string literal.
  4. If using a Symbol-based library, wrap it with middleware that maps Symbols to strings before they reach dispatch.

Example fix

// before
const ADD_TODO = Symbol('add')
dispatch({ type: ADD_TODO, payload: x })  // type is symbol -> throws

// after
const ADD_TODO = 'todos/add' as const
dispatch({ type: ADD_TODO, payload: x })
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof action.type !== 'string') {
  throw new TypeError(`Action type must be a string, got ${typeof action.type}`)
}
store.dispatch(action)

Type guard

const hasStringType = (a): a is { type: string } =>
  a !== null && typeof a === 'object' && typeof (a as any).type === 'string'

Prevention

When it happens

Trigger: dispatch({ type: Symbol('X') }) — using a Symbol type; dispatch({ type: 1 }) numeric type; dispatch({ type: { ns: 'X' } }) object type; a constants module that exports Symbols instead of strings (common in some Flux libraries).

Common situations: Mixing a Flux-constants library that exports Symbols; using enum numeric values from TypeScript as action types; intentional opaque types for namespacing that should instead use string prefixes; migrated code where types changed from string to Symbol.

Related errors


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