expo/expo · error · Error

${declares}, which is set to ${JSON.stringify(value)} — not

Error message

${declares}, which is set to ${JSON.stringify(value)} — not a finite number. An axis takes a number in the range the font declares, such as -10 for "slnt".

What it means

Thrown by assertValidAxes (withFontsAndroid.ts:214) when the value assigned to an axis is not a finite number — i.e. typeof value !== 'number' || !Number.isFinite(value). Strings ('-10'), NaN, Infinity, or booleans are rejected; an axis takes a plain number in the range the font declares, such as -10 for 'slnt'. Note undefined values are skipped, so app.config.ts conditionals may legally produce undefined.

Source

Thrown at packages/expo-font/plugin/src/withFontsAndroid.ts:214

    if (!AXIS_TAG_PATTERN.test(tag)) {
      throw new Error(
        `${declares}, which holds characters no axis tag uses. A tag begins with a letter, then letters, digits, or the trailing spaces that pad a shorter tag. ${consequence}`
      );
    }

    if (
      registeredAxisTags.includes(lowercaseTag) &&
      lowercaseTag !== tag &&
      !isFoundryAxisTag(tag)
    ) {
      throw new Error(
        `${declares}, which names no axis: tags are case sensitive. Write it as ${JSON.stringify(lowercaseTag)}. ${consequence}`
      );
    }

    if (typeof value !== 'number' || !Number.isFinite(value)) {
      throw new Error(
        `${declares}, which is set to ${JSON.stringify(value)} — not a finite number. ` +
          `An axis takes a number in the range the font declares, such as -10 for "slnt".`
      );
    }
  }
}

export function warnAboutUnknownAxisTags(fontsByFamily: GroupedFontObject) {
  for (const { tag, value, declares } of collectDeclaredAxes(fontsByFamily)) {
    if (value === undefined) {
      continue;
    }

    const lowercaseTag = tag.toLowerCase();

    // A font may declare `SLNT`, so this stays legal — but it is far more often `slnt` in the
    // wrong case, and Android then applies nothing.
    if (isFoundryAxisTag(tag) && registeredAxisTags.includes(lowercaseTag)) {

View on GitHub (pinned to da586c407b)

Solutions

  1. Convert values to plain numbers: Number(value) or write literals unquoted
  2. If the value comes from env/CMS, validate with Number.isFinite(Number(raw)) before passing to the plugin
  3. Drop the axis entry (or set it to undefined in a conditional app.config.ts) instead of passing NaN/'-'

Example fix

// before (app.config.ts)
axes: { slnt: process.env.SLANT, wght: Number(process.env.WEIGHT || 'NaN') }
// after
axes: {
  slnt: process.env.SLANT != null ? Number(process.env.SLANT) : undefined,
  wght: Number.isFinite(Number(process.env.WEIGHT)) ? Number(process.env.WEIGHT) : undefined,
}
Defensive patterns

Strategy: validation

Validate before calling

function assertFiniteAxisValues(axes: Record<string, unknown>) {
  for (const [tag, value] of Object.entries(axes)) {
    if (value === undefined) continue; // app.config.ts conditionals may write undefined
    if (typeof value !== 'number' || !Number.isFinite(value)) {
      throw new Error(`Axis ${tag} must be a finite number, got ${JSON.stringify(value)}`);
    }
  }
}

Type guard

const isFiniteAxisValue = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);

Prevention

When it happens

Trigger: Writing axes: { slnt: '-10' } (string from JSON), { wght: NaN }, { opsz: Infinity }, or parsing axis values from a remote config/env as strings in app.config.ts and passing them into the expo-font plugin config.

Common situations: Reading variation settings from a CMS or .env (always strings) without Number() conversion; JSON configs where numbers get quoted; computations producing NaN.

Related errors


AI-assisted analysis of expo/expo@da586c407b (2026-08-23). Data as JSON: /api/errors/8fd1374cafcb7787. Report an issue: GitHub.