actualbudget/actual · error

Unknown budget action type: ${String(type)}

Error message

Unknown budget action type: ${String(type)}

What it means

useBudgetActions' mutation builds a server call via a switch over the budget action type; the default branch throws for any type string not in the known union. It is a developer-facing exhaustiveness error: an action type reached the hook that the switch does not handle. The mutation's onError handler then surfaces a generic notification to the user.

Source

Thrown at packages/desktop-client/src/budget/mutations.ts:896

            month,
            N: 12,
            category: args.category,
          });
          return null;
        case 'copy-single-last':
          await send('budget/copy-single-month', {
            month,
            category: args.category,
          });
          return null;
        case 'copy-until-year-end':
          await send('budget/copy-until-year-end', {
            month,
            category: args.category,
          });
          return null;
        default:
          throw new Error(`Unknown budget action type: ${String(type)}`);
      }
    },
    onSuccess: notification => {
      if (notification) {
        dispatch(
          addNotification({
            notification: translateBudgetTemplateNotification(notification, t),
          }),
        );
      }
    },
    onError: error => {
      console.error('Error applying budget action:', error);
      dispatchErrorNotification(
        dispatch,
        t('There was an error applying the budget action. Please try again.'),
        error,
      );

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Use one of the supported action types handled by the switch (e.g. 'carryover', 'copy-single-last', 'set-single-3-avg')
  2. Fix the typo in the action type string at the call site
  3. If a new action was added, add a case to the switch in useBudgetActions (packages/desktop-client/src/budget/mutations.ts) mapping it to the right 'budget/*' server call
  4. Check the type union definition for budget actions and ensure switch exhaustiveness (a never check in default) so this fails at compile time

Example fix

// before
default:
  throw new Error(`Unknown budget action type: ${String(type)}`);
// after
default: {
  const _exhaustive: never = type;
  throw new Error(`Unknown budget action type: ${String(_exhaustive)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const KNOWN_ACTIONS = ['apply-multiple-templates','carryover','copy-single-last','copy-until-year-end','reset-income-carryover','set-single-3-avg','set-single-6-avg','set-single-12-avg'] as const;
if (!KNOWN_ACTIONS.includes(type)) {
  throw new Error(`Unsupported budget action: ${type}`);
}

Type guard

type BudgetActionType = typeof KNOWN_ACTIONS[number];
function isBudgetActionType(t: string): t is BudgetActionType {
  return (KNOWN_ACTIONS as readonly string[]).includes(t);
}

Try / catch

try {
  await applyBudgetAction.mutateAsync({ type, month, args });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unknown budget action type')) {
    dispatch(addNotification({ notification: { type: 'error', message: 'Unsupported budget action' } }));
  } else throw e;
}

Prevention

When it happens

Trigger: Calling applyBudgetAction.mutate({ type: 'some-new-action', ... }) (or dispatching a budget action through a menu/component) with a type string that is not one of the handled cases, e.g. a typo, a removed/renamed action, or a new action added to the type union without adding a switch case.

Common situations: Adding a new budget action type to the union but forgetting the switch case; typos in template/action strings from budget templates ('copy-single-last' vs 'copy-last'); stale code after refactoring action names.

Related errors


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