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
- Validate and coerce at the data boundary: Number(v), check Number.isFinite, else map to null
- Show a fallback in the template for non-numeric values instead of piping them
- 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
- Coerce and validate numeric fields at the API boundary with Number() + Number.isFinite
- Type models strictly (number or numeric string), never any
- Render a dash/placeholder for sentinel values ('N/A') instead of piping them
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
- INVALID_DIGIT_INFO
- INVALID_NUMBER_OF_DIGITS_AFTER_FRACTION
- INVALID_TO_DATE_CONVERSION
- LocationProvider.search(): First argument must be a string o
AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22).
Data as JSON: /api/errors/cc2c965b9b84ce69.
Report an issue: GitHub.