actualbudget/actual · error
Unrecognized menu option: ${String(name)}
Error message
Unrecognized menu option: ${String(name)} What it means
IncomeMenu's onSelect switch handles its menu options (e.g. 'carryover') and throws on any other name via `Unrecognized menu option: ${String(name)}`. Like BalanceMenu/BudgetMenu, it is an exhaustiveness guard assuming items and switch cases stay synchronized.
Source
Thrown at packages/desktop-client/src/components/budget/envelope/IncomeMenu.tsx:47
return (
<span>
<Menu
onMenuSelect={name => {
switch (name) {
case 'view':
onShowActivity(categoryId, month);
break;
case 'carryover':
if (!carryover) onBudgetAction(month, 'reset-hold');
onBudgetAction(month, 'carryover', {
category: categoryId,
flag: !carryover,
});
onClose();
break;
default:
throw new Error(`Unrecognized menu option: ${String(name)}`);
}
}}
items={[
{
name: 'carryover',
text: carryover ? t('Disable auto hold') : t('Enable auto hold'),
},
{
name: 'view',
text: t('View transactions'),
},
]}
/>
</span>
);
}
View on GitHub (pinned to d4334cb6e6)
Solutions
- Add the missing case to IncomeMenu's onSelect switch for the option name in question.
- Share a string-literal union between item definitions and onSelect so TypeScript reports unmatched names.
- Replace the throw with a logged warning + early return if a no-op option is acceptable in some contexts.
Example fix
// before
default:
throw new Error(`Unrecognized menu option: ${String(name)}`);
// after
case 'reset-income':
onResetIncome?.();
break;
default:
console.warn('Unknown income menu option', name); Defensive patterns
Strategy: type-guard
Validate before calling
type IncomeMenuOption = 'carryover' | 'report-budget'; const INCOME_MENU_OPTIONS: readonly IncomeMenuOption[] = ['carryover', 'report-budget']; if (!INCOME_MENU_OPTIONS.includes(name as IncomeMenuOption)) return;
Type guard
function isIncomeMenuOption(name: string): name is IncomeMenuOption {
return (INCOME_MENU_OPTIONS as readonly string[]).includes(name);
} Try / catch
onSelect={name => {
try {
handleIncomeOption(name);
} catch (e) {
if (String(e.message).startsWith('Unrecognized menu option')) console.warn('unhandled income option', name);
else throw e;
}
}} Prevention
- Share a literal-union type between IncomeMenu items and its onSelect switch.
- Add the switch case whenever a new income menu item is introduced.
- Add a test that exercises each item name against onSelect to keep cases in sync.
When it happens
Trigger: Selecting an income-category menu item whose name has no case in the switch — after adding a new item to the items array without a matching case, or dispatching a stale/renamed option name programmatically.
Common situations: Contributors adding an income menu action but only editing items; renamed option ids surviving in saved/UI state; tests clicking items that no longer have a handler.
Related errors
- Unrecognized menu option: ${name}
- Unrecognized menu item: ${name}
- Unknown item type:
- Unknown display type: ${String(displayType)}
- Unknown template type: ${String(type satisfies undefined)}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/0f0f6b04a9b17d9d.
Report an issue: GitHub.