actualbudget/actual · error

Unsupported schedule status for tooltip: ${scheduleStatus}

Error message

Unsupported schedule status for tooltip: ${scheduleStatus}

What it means

getScheduleStatusDescription maps a schedule's computed status to a localized tooltip description. The switch over scheduleStatus has cases for the known statuses; the default branch throws when a status value arrives that has no tooltip mapping. This fail-fast keeps unmapped statuses from silently rendering an empty tooltip.

Source

Thrown at packages/desktop-client/src/hooks/useCategoryScheduleGoalTemplateIndicator.ts:152

        'Missed {{scheduleName}} due {{distanceFromNow}} ({{formattedDate}})',
        {
          scheduleName: schedule.name,
          distanceFromNow,
          formattedDate,
        },
      );
    case 'due':
    case 'upcoming':
      return t(
        '{{scheduleName}} is due {{distanceFromNow}} ({{formattedDate}})',
        {
          scheduleName: schedule.name,
          distanceFromNow: isToday ? t('today') : distanceFromNow,
          formattedDate,
        },
      );
    default:
      throw new Error(
        `Unsupported schedule status for tooltip: ${scheduleStatus}`,
      );
  }
}

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Log the actual scheduleStatus value and confirm it against the statuses handled in useCategoryScheduleGoalTemplateIndicator.ts
  2. Add a case (or extend the mapping) for the new status in getScheduleStatusDescription
  3. If it's your own code, pass only statuses produced by the shared schedule-status utility rather than hand-built strings
  4. Wrap in try/catch and fall back to a generic tooltip text

Example fix

// before
default:
  throw new Error(`Unsupported schedule status for tooltip: ${scheduleStatus}`);
// after
default:
  return t('Upcoming'); // safe fallback instead of throwing
Defensive patterns

Strategy: fallback

Validate before calling

const KNOWN = ['scheduled','missed','completed','todo' /* match hook's statuses */];
if (!KNOWN.includes(scheduleStatus)) return t('Upcoming');

Type guard

type ScheduleStatus = Parameters<typeof getScheduleStatusDescription>[0];
function isScheduleStatus(v: unknown): v is ScheduleStatus {
  return typeof v === 'string'; /* narrow against the concrete union in the hook */
}

Try / catch

let description: string;
try {
  description = getScheduleStatusDescription(status, schedule, t);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unsupported schedule status')) {
    description = t('Upcoming');
  } else throw e;
}

Prevention

When it happens

Trigger: A scheduleStatus value outside the handled set (e.g. 'scheduled', 'missed', or a newly added status) reaches the function — typically when schedule-goal status computation gains a new variant but the tooltip map isn't updated, or custom template code passes an unexpected status string.

Common situations: Version skew: schedules computed by newer loot-core code viewed in an older client; a plugin or custom report reusing the hook with its own status strings; a typo when passing a status manually.

Related errors


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