{"record":{"id":"b1a4bf1588c2c731","repo":"actualbudget/actual","slug":"unknown-display-type-string-type-satisfies-neve","errorCode":null,"errorMessage":"Unknown display type: ${String(type satisfies never)}","messagePattern":"Unknown display type: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/budget/goals/reducer.ts","lineNumber":364,"sourceCode":"  action: Action,\n): ReducerState => {\n  const type = action.type;\n  switch (type) {\n    case 'set-type':\n      return {\n        ...state,\n        ...changeType(state, action.payload),\n      };\n    case 'set-template':\n      return {\n        ...state,\n        ...getInitialState(action.payload),\n      };\n    case 'update-template':\n      return mapTemplateTypesForUpdate(state, action.payload);\n    default:\n      // Make sure we're not missing any cases\n      throw new Error(`Unknown display type: ${String(type satisfies never)}`);\n  }\n};\n","sourceCodeStart":346,"sourceCodeEnd":367,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/goals/reducer.ts#L346-L367","documentation":"templateReducer is the top-level reducer for the budget goals template editor state. Its default branch throws this when an action's discriminator is not one of the handled action types (e.g. 'set-template', 'update-template', change-type actions). The `type satisfies never` assertion means TypeScript guarantees exhaustiveness over the Action union, so a runtime hit means an action with an unrecognized `type` was dispatched — typically a typo, an action from another reducer, or a stale serialized action.","triggerScenarios":"Dispatching an action to templateReducer whose `type` literal is not part of the goals/actions Action union — e.g. calling dispatch with a hand-written object `{ type: 'update-templates' }`, wiring the wrong reducer to a dispatcher, or dispatching actions from a different feature into this reducer.","commonSituations":"Refactoring action type strings without updating all dispatch sites; copy-pasting a reducer and dispatching foreign actions at it; custom integrations or tests dispatching malformed actions.","solutions":["Log the offending action.type to find which dispatch site sends the unknown action.","Fix the dispatch site to use a valid action type from the Action union in ./actions.","If a new action was added, add a case for it in templateReducer.","Type the dispatcher with React's Dispatch<Action> so TypeScript rejects unknown action types at compile time."],"exampleFix":"// before\ndispatch({ type: 'update-templates' as never });\n// after\ndispatch({ type: 'update-template', payload: template });","handlingStrategy":"type-guard","validationCode":"const ACTION_TYPES = new Set(['set-template','update-template','change-type' /* ...all union members */]);\nif (!ACTION_TYPES.has(action.type)) return state; // ignore unknown actions","typeGuard":"function isGoalsAction(a: { type: string }): a is Action {\n  return GOALS_ACTION_TYPES.has(a.type);\n}","tryCatchPattern":"try {\n  dispatch(action);\n} catch (err) {\n  logger.error('Bad action for templateReducer', action, err);\n}","preventionTips":["Type useReducer with Dispatch<Action> from ./actions so unknown action literals fail compilation.","Never use `as any`/`as never` when dispatching; let TypeScript check action shapes.","Add new action types to the Action union and reducer cases together.","In tests, iterate every member of the Action union against the reducer to prove exhaustiveness."],"tags":["typescript","exhaustiveness","reducer","react"],"backgroundTag":"unhandled-switch-case","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}