actualbudget/actual · error

Unrecognized menu option: ${name}

Error message

Unrecognized menu option: ${name}

What it means

CategoryGroupActionMenu maps its menu selection callback through a switch and throws when the selected `name` does not match any known case. This is an internal exhaustiveness guard: the Menu component emitted an option ID the action menu does not handle. It usually means the items list and the switch handler are out of sync.

Source

Thrown at packages/desktop-client/src/components/mobile/budget/CategoryGroupActionMenu.tsx:31

  onApplyBudgetTemplatesInGroup: () => void;
};
export function CategoryGroupActionMenu({
  onApplyBudgetTemplatesInGroup,
  ...props
}: CategoryGroupActionMenuProps) {
  const { t } = useTranslation();

  const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
  return (
    <Menu
      {...props}
      onMenuSelect={name => {
        switch (name) {
          case 'apply-budget-templates-in-group':
            onApplyBudgetTemplatesInGroup();
            break;
          default:
            throw new Error(`Unrecognized menu option: ${name}`);
        }
      }}
      items={[
        ...(isGoalTemplatesEnabled
          ? [
              {
                name: 'apply-budget-templates-in-group',
                text: t('Overwrite with templates'),
              },
            ]
          : []),
      ]}
    />
  );
}

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Add a switch case for the unrecognized option name in onMenuSelect.
  2. Verify items[] entries and switch cases are kept in sync (a single source of truth for option IDs helps).
  3. Rebuild/reload the client so the menu component and handler come from the same version.
  4. Log the incoming name before the default case during development to catch mismatches early.

Example fix

// before
default:
  throw new Error(`Unrecognized menu option: ${name}`);
// after
case 'new-option':
  onNewOption();
  break;
default: {
  const exhaustive: never = name;
  throw new Error(`Unrecognized menu option: ${String(exhaustive)}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const HANDLED = new Set(['apply-budget-templates-in-group']);
if (!HANDLED.has(name)) {
  console.warn(`Ignoring unknown menu option: ${name}`);
  return;
}

Type guard

type MenuOption = 'apply-budget-templates-in-group';
function isMenuOption(name: string): name is MenuOption {
  return name === 'apply-budget-templates-in-group';
}

Try / catch

try {
  onMenuSelect(name);
} catch (err) {
  if (String(err).startsWith('Unrecognized menu option')) {
    console.warn(`Unknown menu option: ${name}`);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: `onMenuSelect` receives a menu option name not present in the switch (currently only 'apply-budget-templates-in-group' is handled) — e.g. after a new item is added to `items` without a matching case, or a stale/foreign option ID is dispatched.

Common situations: A developer adds a new menu item to `items` (or a feature flag reveals an item) but forgets to add the corresponding switch case; plugin or shortcut code dispatching an old option name after a rename.

Related errors


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