actualbudget/actual · error
Unrecognized menu option: ${name}
Error message
Unrecognized menu option: ${name} What it means
BalanceMenu's onSelect switch handles a fixed set of menu option names (e.g. transfer, cover, carryover) and throws on anything else. It's an exhaustiveness guard: the items array and the switch must stay in sync, and a mismatch is treated as a programming error.
Source
Thrown at packages/desktop-client/src/components/budget/envelope/BalanceMenu.tsx:51
const balance =
useEnvelopeSheetValue(envelopeBudget.catBalance(categoryId)) ?? 0;
return (
<Menu
{...props}
onMenuSelect={name => {
switch (name) {
case 'transfer':
onTransfer?.();
break;
case 'carryover':
onCarryover?.(!carryover);
break;
case 'cover':
onCover?.();
break;
default:
throw new Error(`Unrecognized menu option: ${name}`);
}
}}
items={[
...(balance > 0
? [
{
name: 'transfer',
text: t('Transfer to another category'),
},
]
: []),
...(balance < 0
? [
{
name: 'cover',
text: t('Cover overspending'),
},
]View on GitHub (pinned to d4334cb6e6)
Solutions
- Add the missing `case '<name>':` branch to the onSelect switch matching the item you added to the items array.
- Type the item name union and annotate onSelect's parameter so TypeScript flags unmatched names at compile time.
- If the option is intentionally a no-op for some contexts (e.g. cover without overbudgeted amount), early-return instead of throwing.
Example fix
// before
case 'cover':
onCover?.();
break;
default:
throw new Error(`Unrecognized menu option: ${name}`);
// after
case 'cover':
onCover?.();
break;
case 'transfer':
onTransfer?.();
break;
default:
console.warn('Unknown balance menu option', name); Defensive patterns
Strategy: type-guard
Validate before calling
type BalanceMenuOption = 'transfer' | 'cover' | 'carryover' | 'hold';
function isBalanceMenuOption(name: string): name is BalanceMenuOption {
return ['transfer', 'cover', 'carryover', 'hold'].includes(name);
}
if (!isBalanceMenuOption(name)) return; Type guard
function isBalanceMenuOption(name: string): name is BalanceMenuOption {
return ['transfer', 'cover', 'carryover', 'hold'].includes(name);
} Try / catch
onSelect={name => {
try {
handleOption(name);
} catch (e) {
if (String(e.message).startsWith('Unrecognized menu option')) console.warn(name);
else throw e;
}
}} Prevention
- Keep the items array and the switch cases derived from one shared union type.
- Add a case at the same time you add a menu item — never one without the other.
- Use a switch with `name satisfies never` exhaustiveness checking so the compiler catches gaps.
When it happens
Trigger: Calling the menu's onSelect with a name that isn't one of the defined items — usually a custom Menu item added by a caller/parent, or a renamed option where the switch wasn't updated.
Common situations: A developer adds an item to the items array but forgets the switch case; a plugin/patched build injects extra options; refactoring item names in one place only.
Related errors
- Unrecognized menu item: ${name}
- Unrecognized menu option: ${String(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/1983943c3915bd6f.
Report an issue: GitHub.