actualbudget/actual · error
Unrecognized menu option: ${String(name)}
Error message
Unrecognized menu option: ${String(name)} What it means
BalanceMenu (tracking budget) currently supports exactly one action, 'carryover'; its onMenuSelect switch throws `Unrecognized menu option: ${String(name)}` for any other name. Any item added to the items array without a corresponding case triggers this crash on click.
Source
Thrown at packages/desktop-client/src/components/budget/tracking/BalanceMenu.tsx:37
export function BalanceMenu({
categoryId,
onCarryover,
...props
}: BalanceMenuProps) {
const { t } = useTranslation();
const carryover = useTrackingSheetValue(
trackingBudget.catCarryover(categoryId),
);
return (
<Menu
{...props}
onMenuSelect={name => {
switch (name) {
case 'carryover':
onCarryover?.(!carryover);
break;
default:
throw new Error(`Unrecognized menu option: ${String(name)}`);
}
}}
items={[
{
name: 'carryover',
text: carryover
? t('Remove overspending rollover')
: t('Rollover overspending'),
},
]}
/>
);
}
View on GitHub (pinned to d4334cb6e6)
Solutions
- Add a `case` for the new option name in BalanceMenu's onMenuSelect switch
- Ensure the item's `name` is exactly 'carryover' (or whatever handlers exist)
- Replace the throw with a warning default if the menu is expected to grow dynamically
Example fix
// before
default:
throw new Error(`Unrecognized menu option: ${String(name)}`);
// after
case 'transfer-to-sinking':
onTransfer?.();
break;
default:
throw new Error(`Unrecognized menu option: ${String(name)}`); Defensive patterns
Strategy: validation
Validate before calling
if (name !== 'carryover') {
console.warn(`BalanceMenu ignoring unsupported option: ${String(name)}`);
return;
} Type guard
type BalanceMenuOption = 'carryover';
function isBalanceMenuOption(name: string): name is BalanceMenuOption {
return name === 'carryover';
} Try / catch
try {
handleBalanceAction(name);
} catch (e) {
if (String(e?.message).startsWith('Unrecognized menu option')) {
logger.warn('unknown balance menu option ignored');
return;
}
throw e;
} Prevention
- Type the items array with a name union matching the switch so adding an item without a handler fails typecheck
- When porting items from the envelope menu variant, port the handlers at the same time
- Add an exhaustiveness assertion in the default branch during development
- Add a test that clicks every rendered menu item to assert no case is missing
When it happens
Trigger: A BalanceMenu item whose name is not 'carryover' is selected — typically after adding a new menu item (e.g. a transfer or hold action from the envelope variant) without extending the switch, or passing items with mismatched names.
Common situations: Porting menu items from the envelope BudgetMenu variant to the tracking BalanceMenu without porting handlers; renames/typos in the item name; plugin-injected menu entries.
Related errors
- Unrecognized menu option: ${name}
- Unrecognized menu option: ${name}
- InitialFocus expects a single valid React element as its chi
- Unknown budget action type: ${String(type)}
- Unknown display type: ${String(type satisfies never)}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/c8fddf633c66a1e6.
Report an issue: GitHub.