actualbudget/actual · error
Unrecognized menu option: ${name}
Error message
Unrecognized menu option: ${name} What it means
BudgetMonthMenu's onSelect switch throws when the selected menu item's name does not match any of its known options (copy-last, set-zero, copy-3-avg, etc., plus the template actions like apply-goal-template / overwrite-goal-template). Like BudgetMenu, it's an invariant guard that the items list and the selection handler stay in sync.
Source
Thrown at packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetMonthMenu.tsx:62
onSetMonthsAverage(3);
break;
case 'set-6-avg':
onSetMonthsAverage(6);
break;
case 'set-12-avg':
onSetMonthsAverage(12);
break;
case 'check-templates':
onCheckTemplates();
break;
case 'apply-goal-template':
onApplyBudgetTemplates();
break;
case 'overwrite-goal-template':
onOverwriteWithBudgetTemplates();
break;
default:
throw new Error(`Unrecognized menu option: ${name}`);
}
}}
items={[
{ name: 'copy-last', text: t("Copy last month's budget") },
{ name: 'set-zero', text: t('Set budgets to zero') },
{
name: 'set-3-avg',
text: t('Set budgets to 3 month average'),
},
{
name: 'set-6-avg',
text: t('Set budgets to 6 month average'),
},
{
name: 'set-12-avg',
text: t('Set budgets to 12 month average'),
},
...(isGoalTemplatesEnabledView on GitHub (pinned to d4334cb6e6)
Solutions
- Add the missing `case '<name>'` in the onSelect switch at packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetMonthMenu.tsx (around line 62).
- Align the item's `name` in the `items` array with an existing case string.
- Downgrade the default branch to a warning log instead of throwing so the month menu can't crash the budget page.
Example fix
// before
case 'overwrite-goal-template':
onOverwriteWithBudgetTemplates();
break;
default:
throw new Error(`Unrecognized menu option: ${name}`);
// after
case 'overwrite-goal-template':
onOverwriteWithBudgetTemplates();
break;
case 'apply-goal-template':
onApplyBudgetTemplates();
break;
default:
console.warn(`Unrecognized menu option: ${name}`); Defensive patterns
Strategy: validation
Validate before calling
const KNOWN_OPTIONS = ['copy-last','set-zero','copy-3-avg','copy-6-avg','copy-12-avg','apply-goal-template','overwrite-goal-template']; const safeItems = items.filter(item => KNOWN_OPTIONS.includes(item.name));
Type guard
function isMonthMenuOption(name: string): name is 'copy-last' | 'set-zero' | 'copy-3-avg' | 'copy-6-avg' | 'copy-12-avg' | 'apply-goal-template' | 'overwrite-goal-template' {
return ['copy-last','set-zero','copy-3-avg','copy-6-avg','copy-12-avg','apply-goal-template','overwrite-goal-template'].includes(name);
} Try / catch
try {
handleSelect(name);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Unrecognized menu option')) {
logger.warn(`Skipping unknown month menu option: ${name}`);
} else {
throw err;
}
} Prevention
- Use a single string-literal union type for option names shared between items and onSelect.
- Keep feature-flag-gated items and their switch cases adjacent so removals stay in sync.
- Wrap the switch in a Record<name, handler> map for exhaustiveness checking.
When it happens
Trigger: Selecting a month-menu option whose `name` is not handled in the switch — e.g. a caller adds a custom item to the menu, or a feature flag gates an item (like 'apply-goal-template') whose case was removed/renamed, then the user clicks it.
Common situations: Feature-flag mismatch: item shown under goalTemplatesEnabled while its case lives behind a differently-named branch; plugin/fork injecting extra options; renaming item names during refactor while older UI code (or saved shortcuts) still sends the old name.
Related errors
- Unrecognized menu item: ${name}
- Unrecognized menu option: ${String(item)}
- Unrecognized menu option: ${String(name)}
- Unrecognized menu item: ${name}
- Unrecognized menu option: ${String(name)}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/a26795b279648053.
Report an issue: GitHub.