actualbudget/actual · error

Cannot delete rule: invalid id

Error message

Cannot delete rule: invalid id

What it means

MobileRuleEditPage's handleDelete guard throws when the route-supplied rule `id` is missing or equals the literal 'new'. Deleting only makes sense for a persisted rule, so the code defends against the edit page being opened in create mode (id 'new') or without an id and then the user tapping Delete. It is a defensive runtime guard for the modal-flow navigation.

Source

Thrown at packages/desktop-client/src/components/mobile/rules/MobileRuleEditPage.tsx:112

  const handleSave = () => {
    if (rule?.id) {
      showUndoNotification({
        message: t('Rule saved successfully'),
      });
    }
    // Navigate back to rules list
    void navigate('/rules');
  };

  const handleCancel = () => {
    void navigate(-1);
  };

  const handleDelete = () => {
    // Runtime guard to ensure id exists
    if (!id || id === 'new') {
      throw new Error('Cannot delete rule: invalid id');
    }

    dispatch(
      pushModal({
        modal: {
          name: 'confirm-delete',
          options: {
            message: t('Are you sure you want to delete this rule?'),
            onConfirm: async () => {
              try {
                await send('rule-delete', id);
                showUndoNotification({
                  message: t('Rule deleted successfully'),
                });
                void navigate('/rules');
              } catch (error) {
                console.error('Failed to delete rule:', error);
                dispatch(

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Only render/enable the Delete action when a persisted rule id exists (id present and !== 'new').
  2. If in create mode ('new'), delete should just navigate back — fix the entry point that shows Delete for unsaved rules.
  3. Verify the route/navigation code passes the rule id param correctly to MobileRuleEditPage.
  4. Wrap the delete flow in a check so users get a friendly message instead of a crash.

Example fix

// before
const handleDelete = () => {
  if (!id || id === 'new') {
    throw new Error('Cannot delete rule: invalid id');
  }
// after
const handleDelete = () => {
  if (!id || id === 'new') {
    void navigate(-1); // nothing persisted to delete
    return;
  }
Defensive patterns

Strategy: validation

Validate before calling

const canDelete = Boolean(id) && id !== 'new';
if (!canDelete) {
  void navigate(-1);
  return;
}
// safe to open delete confirmation

Type guard

function isPersistedRuleId(id: string | undefined): id is string {
  return typeof id === 'string' && id !== '' && id !== 'new';
}

Try / catch

try {
  handleDelete();
} catch (err) {
  if (String(err).includes('invalid id')) {
    void navigate(-1); // nothing to delete
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: User taps the Delete button on the mobile rule edit page while the route param `id` is undefined or 'new' — i.e. the page was opened to create a rule, or navigation happened without an id.

Common situations: Deep-linking or navigating to the rule edit page without an id; a stale UI entry point that opens the edit page in create mode; a routing bug after upgrading the app where the delete button is rendered in 'new' mode.

Related errors


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