{"record":{"id":"baacb422b6fa9dc5","repo":"actualbudget/actual","slug":"unhandled-action-type-action-type","errorCode":null,"errorMessage":"Unhandled action type: ${action.type}","messagePattern":"Unhandled action type: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/filters/updateFilterReducer.ts","lineNumber":54,"sourceCode":"        // Convert single value to array when switching to oneOf/notOneOf\n        if (value === null || value === undefined) {\n          value = [];\n        } else if (!Array.isArray(value)) {\n          // @ts-expect-error - fix me\n          value = [value];\n        }\n      }\n      return { ...state, op: action.op, value };\n    }\n    case 'set-value': {\n      const { value } = makeValue(action.value, {\n        type: FIELD_TYPES.get(state.field),\n      });\n      return { ...state, value };\n    }\n    default:\n      // @ts-expect-error - fix me\n      throw new Error(`Unhandled action type: ${action.type}`);\n  }\n}\n","sourceCodeStart":36,"sourceCodeEnd":57,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/filters/updateFilterReducer.ts#L36-L57","documentation":"updateFilterReducer handles only two action types: 'set-op' and 'set-value'. Its default branch throws this when dispatched with any other action type. The `@ts-expect-error` marker shows the action union is not enforced strictly here, so TypeScript cannot prevent invalid actions — making this a runtime-only guard. It protects the filter-conditions UI state machine from unknown messages.","triggerScenarios":"Dispatching an action other than 'set-op'/'set-value' into a useReducer wired to updateFilterReducer — e.g. `{ type: 'set-field' }`, a typo like 'setop', or reusing this reducer for another filter feature with different action names.","commonSituations":"Copy-pasting dispatch calls from another filter reducer with a richer action set; renaming action types at one call site but not the dispatch site; extending the filter UI with new actions and forgetting to add reducer cases.","solutions":["Log action.type in the error to identify the bogus dispatch.","Correct the dispatch site to send only 'set-op' or 'set-value'.","If a new behavior is needed (e.g. clearing a value), add an explicit case to updateFilterReducer and to the action union.","Tighten the reducer's action parameter type so TypeScript rejects unknown action types (removing the need for @ts-expect-error)."],"exampleFix":"// before\ndispatch({ type: 'set-field', field: 'date' } as any);\n// after\ndispatch({ type: 'set-op', op: 'is' });\ndispatch({ type: 'set-value', value: '2024-01-01' });","handlingStrategy":"type-guard","validationCode":"type FilterAction = { type: 'set-op'; op: RuleConditionEntity['op'] } | { type: 'set-value'; value: unknown };\nif (action.type !== 'set-op' && action.type !== 'set-value') return state;","typeGuard":"function isFilterAction(a: unknown): a is FilterAction {\n  return (\n    typeof a === 'object' && a !== null &&\n    ((a as FilterAction).type === 'set-op' || (a as FilterAction).type === 'set-value')\n  );\n}","tryCatchPattern":"try {\n  dispatch(action);\n} catch (err) {\n  if (String(err).startsWith('Unhandled action type')) {\n    logger.error('Invalid filter action dispatched', action);\n    return;\n  }\n  throw err;\n}","preventionTips":["Give updateFilterReducer a discriminated-union action parameter and remove @ts-expect-error so bad dispatches fail at compile time.","Centralize filter action creators and only dispatch via them.","When extending the filter UI, update the action union and reducer cases together.","Review copy-pasted dispatch calls from other reducers for action-name mismatches."],"tags":["react","reducer","filters","typescript"],"backgroundTag":"unhandled-switch-case","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}