angular/angular · error · RuntimeError

VALUE_NOT_A_NUMBER

VALUE_NOT_A_NUMBER

Error message

${value} is not a number

What it means

The number pipes (DecimalPipe, PercentPipe, CurrencyPipe) accept number or numeric-string input. strToNumber() converts strings whose Number(v) and parseFloat(v) agree; anything that falls through — non-numeric strings like 'abc' or '1.2.3', objects, arrays, booleans — throws VALUE_NOT_A_NUMBER. Null, undefined, '' and NaN are filtered earlier and render as null.

Source

Thrown at packages/common/src/pipes/number_pipe.ts:330

      throw invalidPipeArgumentError(CurrencyPipe, (error as Error).message);
    }
  }
}

function isValue(value: number | string | null | undefined): value is number | string {
  return !(value == null || value === '' || value !== value);
}

/**
 * Transforms a string into a number (if needed).
 */
function strToNumber(value: number | string): number {
  // Convert strings to numbers
  if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) {
    return Number(value);
  }
  if (typeof value !== 'number') {
    throw new RuntimeError(
      RuntimeErrorCode.VALUE_NOT_A_NUMBER,
      ngDevMode && `${value} is not a number`,
    );
  }
  return value;
}

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Validate and coerce at the data boundary: Number(v), check Number.isFinite, else map to null
  2. Show a fallback in the template for non-numeric values instead of piping them
  3. For decimal-comma strings, parse with a dedicated pipe before the number pipe

Example fix

// before
{{ item.price | number:'1.2-2' }} <!-- '1.234,56' or 'N/A' throws -->

// after
export function toNumber(v: unknown): number | null {
  if (typeof v === 'number') return Number.isFinite(v) ? v : null;
  if (typeof v === 'string') { const n = Number(v.replace(',', '.')); return Number.isFinite(n) ? n : null; }
  return null;
}
{{ toNumber(item.price) | number:'1.2-2' }}
Defensive patterns

Strategy: type-guard

Validate before calling

function isNumericInput(v: unknown): boolean {
  if (v == null || v === '') return true;  // pipes render these as empty
  if (typeof v === 'number') return true;
  if (typeof v === 'string') {
    const n = Number(v);
    return Number.isFinite(n) && n === parseFloat(v);
  }
  return false;
}

Type guard

function isParsableNumber(v: unknown): v is number | string {
  if (typeof v === 'number') return true;
  if (typeof v === 'string') { const n = Number(v); return Number.isFinite(n); }
  return false;
}

Try / catch

transform(value: unknown, digits?: string): string | null {
  if (!isParsableNumber(value)) return null;
  return this.decimal.transform(value as number, digits);
}

Prevention

When it happens

Trigger: {{ price | number:'1.2-2' }} where price is 'N/A', '1.234,56' (decimal-comma string), an object, or a boolean; values arriving typed as any from a backend and bound straight into a number pipe.

Common situations: An API starts returning a sentinel string ('N/A', '-') or an object instead of a number; locale-formatted numeric strings (comma decimals) not normalized; optional fields typed number but actually something else.

Related errors


AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22). Data as JSON: /api/errors/cc2c965b9b84ce69. Report an issue: GitHub.