{"record":{"id":"7392221e96ff9026","repo":"actualbudget/actual","slug":"unknown-display-type-string-displaytype","errorCode":null,"errorMessage":"Unknown display type: ${String(displayType)}","messagePattern":"Unknown display type: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/budget/goals/displayTemplateMeta.ts","lineNumber":100,"sourceCode":"    case 'remainder':\n      return {\n        label: t('Whatever is left'),\n        description: t(\n          'Split any remaining To Budget across these categories.',\n        ),\n        icon: SvgShare,\n      };\n    case 'goal':\n      return {\n        label: t('Long-term goal'),\n        description: t(\n          'Set a long-term savings target. This changes the coloring of the balance on the budget page to be based on progress towards the target rather than the current month funding progress.',\n        ),\n        icon: SvgFlag,\n      };\n    default:\n      displayType satisfies never;\n      throw new Error(`Unknown display type: ${String(displayType)}`);\n  }\n}\n","sourceCodeStart":82,"sourceCodeEnd":103,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/goals/displayTemplateMeta.ts#L82-L103","documentation":"getDisplayTemplateMeta maps each DisplayTemplateType to label/description/icon metadata. The default arm uses `displayType satisfies never` as an exhaustiveness assertion: at runtime, receiving a value outside the known union means corrupted or unhandled display type state, so it throws with the raw value in the message.","triggerScenarios":"Calling getDisplayTemplateMeta with a displayType string not in the union ('fixed'|'schedule'|'by'|'percentage'|'historical'|'limit'|'refill'|'remainder'|'goal') — e.g. state loaded from older persisted data or a hand-built ReducerState with a typo.","commonSituations":"Version migration where a display type was renamed/removed but old template state still holds the old value; plugin/custom code constructing a ReducerState manually; a new DisplayTemplateType added to the union without a case here (caught by satisfies never at compile time, but not in JS builds).","solutions":["Fix the displayType value stored in the reducer/template state to a valid union member","Add a case for any newly introduced DisplayTemplateType in the switch","Clear or migrate stale template state (e.g. re-save the category template) after upgrading Actual","If the value comes from user-supplied input, validate it against the union before calling"],"exampleFix":"// before\nconst meta = getDisplayTemplateMeta(someState.displayType);\n// after\nconst KNOWN = ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'] as const;\nconst meta = KNOWN.includes(someState.displayType)\n  ? getDisplayTemplateMeta(someState.displayType)\n  : getDisplayTemplateMeta('fixed');","handlingStrategy":"type-guard","validationCode":"const KNOWN_DISPLAY_TYPES = ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'] as const;\nif (!KNOWN_DISPLAY_TYPES.includes(displayType)) {\n  throw new Error(`Unsupported display type: ${displayType}`);\n}\nconst meta = getDisplayTemplateMeta(displayType);","typeGuard":"const isDisplayTemplateType = (v: string): v is DisplayTemplateType =>\n  ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'].includes(v);","tryCatchPattern":"let meta;\ntry {\n  meta = getDisplayTemplateMeta(displayType);\n} catch (e) {\n  logger.warn('Unknown display type, falling back', displayType);\n  meta = getDisplayTemplateMeta('fixed');\n}","preventionTips":["Rely on the `satisfies never` exhaustiveness check to catch new union members at compile time","Validate persisted displayType values after version migrations","Never construct ReducerState.displayType from unchecked strings"],"tags":["typescript","exhaustiveness","budget-templates","client-side"],"backgroundTag":"unknown-enum-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}