{"record":{"id":"3e05304ef3fbaeca","repo":"actualbudget/actual","slug":"unrecognized-menu-option-string-item","errorCode":null,"errorMessage":"Unrecognized menu option: ${String(item)}","messagePattern":"Unrecognized menu option: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/HelpMenu.tsx","lineNumber":106,"sourceCode":"  const handleItemSelect = (item: HelpMenuItem) => {\n    switch (item) {\n      case 'docs':\n        openDocsForCurrentPage();\n        break;\n      case 'discord':\n        window.Actual.openURLInBrowser('https://discord.gg/pRYNYr4W5A');\n        break;\n      case 'keyboard-shortcuts':\n        dispatch(pushModal({ modal: { name: 'keyboard-shortcuts' } }));\n        break;\n      case 'start-tour':\n        startTour();\n        break;\n      case 'goal-templates':\n        dispatch(pushModal({ modal: { name: 'goal-templates' } }));\n        break;\n      default:\n        throw new Error(`Unrecognized menu option: ${String(item)}`);\n    }\n  };\n\n  useHotkeys('?', () => setMenuOpen(true), { useKey: true });\n\n  return (\n    <SpaceBetween>\n      <HelpButton ref={menuButtonRef} onPress={toggleMenuOpen} />\n\n      <Popover\n        placement=\"bottom end\"\n        offset={8}\n        triggerRef={menuButtonRef}\n        isOpen={isMenuOpen}\n        onOpenChange={() => setMenuOpen(false)}\n      >\n        <Menu\n          onMenuSelect={(item: HelpMenuItem) => {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/HelpMenu.tsx#L88-L124","documentation":"HelpMenu's handleItemSelect switches over the selected menu item's name and dispatches the corresponding action (open URL, start tour, push modal, etc.). Any item name that reaches the default branch is not one of the known options, so the component throws 'Unrecognized menu option' to surface the programming mistake loudly rather than silently doing nothing.","triggerScenarios":"Adding a new item to the HelpMenu's items list (or onSelect invocation) without adding a matching case in handleItemSelect; passing an item with a changed or renamed name; a typo in the item name; a stale cached item from a plugin/extension of the menu.","commonSituations":"Contributing a new help menu entry and forgetting the switch case; renaming an existing item in one place only; a stale client bundle serving an outdated menu.","solutions":["Add a `case` for the new item name in handleItemSelect before dispatching.","Check for typos and keep item names in the items array in sync with the switch cases.","Type the menu item names as a union so TypeScript flags unhandled names at compile time.","Rebuild/reload the client if a stale bundle is serving an outdated menu.","If you only meant to trigger the help menu, use the '?' hotkey or a known item name (e.g. 'goal-templates')."],"exampleFix":"// before\ncase 'goal-templates':\n  dispatch(pushModal({ modal: { name: 'goal-templates' } }));\n  break;\ndefault:\n  throw new Error(`Unrecognized menu option: ${String(item)}`);\n// after\ncase 'my-new-item':\n  dispatch(openMyNewItem());\n  break;\ncase 'goal-templates':\n  dispatch(pushModal({ modal: { name: 'goal-templates' } }));\n  break;","handlingStrategy":"type-guard","validationCode":"type HelpMenuItem = 'settings-file' | 'goal-templates' | 'start-tour';\nconst knownItems: readonly HelpMenuItem[] = ['settings-file', 'goal-templates', 'start-tour'];\nfunction isKnownHelpItem(item) {\n  return knownItems.includes(item);\n}\nif (!isKnownHelpItem(item)) return; // skip dispatch","typeGuard":"function isHelpMenuItem(value) {\n  return typeof value === 'string' && ['settings-file','goal-templates','start-tour'].includes(value);\n}","tryCatchPattern":"try {\n  handleItemSelect(item);\n} catch (e) {\n  if (e.message.startsWith('Unrecognized menu option')) {\n    logger.warn('Ignoring unknown help menu item', item);\n    return;\n  }\n  throw e;\n}","preventionTips":["Type menu item names as a string union shared between the items list and the switch.","Exhaustive-switch with a `satisfies never` check so new items force a case update.","Keep item definitions and handleItemSelect cases in the same file/adjacent code.","Test the HelpMenu click path for every defined item."],"tags":["react","ui","menu","developer-error"],"backgroundTag":"unhandled-switch-case","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}