actualbudget/actual · error

Unknown display type: ${String(displayType)}

Error message

Unknown display type: ${String(displayType)}

What it means

getDisplayTemplateMeta maps each DisplayTemplateType to label/description/icon metadata. The default arm uses `displayType satisfies never` as an exhaustiveness assertion: at runtime, receiving a value outside the known union means corrupted or unhandled display type state, so it throws with the raw value in the message.

Source

Thrown at packages/desktop-client/src/components/budget/goals/displayTemplateMeta.ts:100

    case 'remainder':
      return {
        label: t('Whatever is left'),
        description: t(
          'Split any remaining To Budget across these categories.',
        ),
        icon: SvgShare,
      };
    case 'goal':
      return {
        label: t('Long-term goal'),
        description: t(
          'Set a long-term savings target. This changes the coloring of the balance on the budget page to be based on progress towards the target rather than the current month funding progress.',
        ),
        icon: SvgFlag,
      };
    default:
      displayType satisfies never;
      throw new Error(`Unknown display type: ${String(displayType)}`);
  }
}

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Fix the displayType value stored in the reducer/template state to a valid union member
  2. Add a case for any newly introduced DisplayTemplateType in the switch
  3. Clear or migrate stale template state (e.g. re-save the category template) after upgrading Actual
  4. If the value comes from user-supplied input, validate it against the union before calling

Example fix

// before
const meta = getDisplayTemplateMeta(someState.displayType);
// after
const KNOWN = ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'] as const;
const meta = KNOWN.includes(someState.displayType)
  ? getDisplayTemplateMeta(someState.displayType)
  : getDisplayTemplateMeta('fixed');
Defensive patterns

Strategy: type-guard

Validate before calling

const KNOWN_DISPLAY_TYPES = ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'] as const;
if (!KNOWN_DISPLAY_TYPES.includes(displayType)) {
  throw new Error(`Unsupported display type: ${displayType}`);
}
const meta = getDisplayTemplateMeta(displayType);

Type guard

const isDisplayTemplateType = (v: string): v is DisplayTemplateType =>
  ['fixed','schedule','by','percentage','historical','limit','refill','remainder','goal'].includes(v);

Try / catch

let meta;
try {
  meta = getDisplayTemplateMeta(displayType);
} catch (e) {
  logger.warn('Unknown display type, falling back', displayType);
  meta = getDisplayTemplateMeta('fixed');
}

Prevention

When it happens

Trigger: Calling getDisplayTemplateMeta with a displayType string not in the union ('fixed'|'schedule'|'by'|'percentage'|'historical'|'limit'|'refill'|'remainder'|'goal') — e.g. state loaded from older persisted data or a hand-built ReducerState with a typo.

Common situations: Version migration where a display type was renamed/removed but old template state still holds the old value; plugin/custom code constructing a ReducerState manually; a new DisplayTemplateType added to the union without a case here (caught by satisfies never at compile time, but not in JS builds).

Related errors


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