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

  1. Add a `case` for the new option name in BalanceMenu's onMenuSelect switch
  2. Ensure the item's `name` is exactly 'carryover' (or whatever handlers exist)
  3. 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

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


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/c8fddf633c66a1e6. Report an issue: GitHub.