actualbudget/actual · error

Unrecognized menu option: ${name}

Error message

Unrecognized menu option: ${name}

What it means

BudgetMonthMenu renders a dropdown of budget actions (copy last month, set to zero, apply templates, etc.). Its onMenuSelect handler is a switch over the selected item's `name`; if the selected name has no case, the default branch throws `Unrecognized menu option: ${name}`. This is an internal consistency guard: the items array and the switch must stay in sync, and this crash signals they did not.

Source

Thrown at packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetMonthMenu.tsx:67

            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;
          case 'cleanup-goal-template':
            onEndOfMonthCleanup();
            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'),
        },
        ...(isGoalTemplatesEnabled

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Add a `case` for the missing option name in the onMenuSelect switch in BudgetMonthMenu.tsx
  2. Fix the typo/wrong name in the menu item so it matches an existing case exactly
  3. Make the default branch a console warning (or no-op) instead of a throw if extensibility is intended

Example fix

// before
default:
  throw new Error(`Unrecognized menu option: ${name}`);
// after
case 'apply-budget-template':
  onApplyBudgetTemplates();
  break;
default:
  throw new Error(`Unrecognized menu option: ${name}`);
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN = ['copy-last','set-zero','check-templates','overwrite-with-templates','cleanup-goal-template'];
if (!KNOWN.includes(item.name)) {
  console.warn(`Skipping unknown budget menu option: ${item.name}`);
  return;
}

Type guard

type BudgetMenuOption = 'copy-last' | 'set-zero' | 'check-templates' | 'overwrite-with-templates' | 'cleanup-goal-template';
function isBudgetMenuOption(name: string): name is BudgetMenuOption {
  return ['copy-last','set-zero','check-templates','overwrite-with-templates','cleanup-goal-template'].includes(name);
}

Try / catch

try {
  renderApp();
} catch (e) {
  if (String(e?.message).startsWith('Unrecognized menu option')) {
    logger.warn({ name: 'menu-option-fallback' }, 'unknown menu option ignored');
  } else throw e;
}

Prevention

When it happens

Trigger: A MenuItem whose `name` is not one of 'copy-last', 'set-zero', 'check-templates', 'overwrite-with-templates', 'cleanup-goal-template' is dispatched to onMenuSelect — e.g. a newly added menu item without a matching switch case, a plugin/custom item injected into the items list, or a stale/typo'd name string.

Common situations: Developers adding a new budget action to the `items` array but forgetting to add the corresponding `case` in the switch; rebasing code where a case was renamed in one place but not the other; third-party code overriding the menu items.

Related errors


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