actualbudget/actual · error

Unknown format type: ${type}

Error message

Unknown format type: ${type}

What it means

format() switches only over the documented FormatType union ('string', 'number', 'percentage', 'financial', 'financial-with-sign', 'financial-no-decimals'). Any other type value reaches the default branch and throws 'Unknown format type: <type>'. It protects against typos and stale string literals passed as the format type.

Source

Thrown at packages/desktop-client/src/hooks/useFormat.ts:106

      }

      if (typeof localValue !== 'number') {
        throw new Error(
          'Value is not a number (' + typeof localValue + '): ' + localValue,
        );
      }

      return {
        numericValue: localValue,
        formattedString: integerToCurrency(
          localValue,
          formatter,
          decimalPlaces,
        ),
      };
    }
    default:
      throw new Error('Unknown format type: ' + type);
  }
}

export function useFormat(): UseFormatResult {
  const [numberFormatPref] = useSyncedPref('numberFormat');
  const [hideFractionPref] = useSyncedPref('hideFraction');
  const [defaultCurrencyCodePref] = useSyncedPref('defaultCurrencyCode');
  const [symbolPositionPref] = useSyncedPref('currencySymbolPosition');
  const [spaceEnabledPref] = useSyncedPref(
    'currencySpaceBetweenAmountAndSymbol',
  );

  const activeCurrency = useMemo(() => {
    return getCurrency(defaultCurrencyCodePref || '');
  }, [defaultCurrencyCodePref]);

  const numberFormatConfig = useMemo(
    () =>

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Use only the FormatType union values: string, number, percentage, financial, financial-with-sign, financial-no-decimals
  2. Import and use the FormatType type so TypeScript rejects invalid literals at compile time (avoid typing the arg as string/any)
  3. If you need a new type, extend the FormatType union and the switch in useFormat.ts rather than passing a custom string
  4. Map legacy type names to current ones at the boundary before calling

Example fix

// before
format(value, 'currency'); // not a FormatType
// after
import type { FormatType } from './useFormat';
const type: FormatType = 'financial';
format(value, type);
Defensive patterns

Strategy: type-guard

Validate before calling

const FORMAT_TYPES = ['string','number','percentage','financial','financial-with-sign','financial-no-decimals'] as const;
if (!FORMAT_TYPES.includes(type as FormatType)) throw new Error(`Unknown format type: ${type}`);

Type guard

function isFormatType(v: unknown): v is FormatType {
  return typeof v === 'string' && ['string','number','percentage','financial','financial-with-sign','financial-no-decimals'].includes(v);
}

Try / catch

let out: string;
try {
  out = format(value, type);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unknown format type')) {
    console.warn(`Unknown format type ${type}, defaulting to string`);
    out = format(value, 'string');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling format(v, 'currency') or format(v, 'int') — names from other libraries or older Actual versions; a typo like 'financail'; a string from config/props typed as any where a FormatType is expected.

Common situations: Copy-pasting formatter calls from other codebases; migrating from an older API that used different type names; reading the format type from user-editable config without whitelisting.

Related errors


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