actualbudget/actual · error
Value is not a number (${typeof value}): ${value}
Error message
Value is not a number (${typeof value}): ${value} What it means
The format function's 'number' case requires the raw value to be a JS number so Intl-based formatter.format(value) can run. If value is a string, null, undefined, object, etc., it throws 'Value is not a number (<typeof>): <value>'. Note 'financial' types coerce strings, but 'number' deliberately does not.
Source
Thrown at packages/desktop-client/src/hooks/useFormat.ts:59
function format(
value: unknown,
type: FormatType,
formatter: { format: (value: number) => string },
decimalPlaces: number,
): FormatResult {
switch (type) {
case 'string': {
const val = JSON.stringify(value);
if (val.charAt(0) === '"' && val.charAt(val.length - 1) === '"') {
return { formattedString: val.slice(1, -1) };
}
return { formattedString: val };
}
case 'number':
if (typeof value !== 'number') {
throw new Error(
'Value is not a number (' + typeof value + '): ' + value,
);
}
return { numericValue: value, formattedString: formatter.format(value) };
case 'percentage':
return { formattedString: value + '%' };
case 'financial-with-sign':
case 'financial-no-decimals':
case 'financial': {
let localValue = value;
if (localValue == null || localValue === '') {
localValue = 0;
} else if (typeof localValue === 'string') {
// This case is generally flawed, but we need to support it for
// backwards compatibility for now.
// For example, it is not clear how the string might look like
// The Budget sends 12300, if the user inputs 123.00, but
// there might be other components that send 123 with the same user input.View on GitHub (pinned to d4334cb6e6)
Solutions
- Coerce the value before formatting: Number(value) after checking it's not NaN
- Pass the 'financial' type instead if the value may be an integer-amount string and currency formatting is acceptable
- Fix the data source (query select / API mapping) to return numbers, not strings
- Guard the call site: typeof v === 'number' ? format(v, 'number') : format(0, 'number')
Example fix
// before format(row.total as unknown, 'number'); // row.total is '123' (string) // after const n = Number(row.total); format(Number.isFinite(n) ? n : 0, 'number');
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof value !== 'number' || !Number.isFinite(value)) {
throw new TypeError(`format('number') requires a finite number, got ${typeof value}`);
} Type guard
function isNumeric(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v);
} Try / catch
let out: string;
try {
out = format(value, 'number');
} catch (e) {
if (e instanceof Error && e.message.startsWith('Value is not a number')) {
out = format(0, 'number');
} else throw e;
} Prevention
- Coerce API/query results to numbers at the data layer, not at render time
- Use the FormatType union, not raw strings, so mismatches surface in review
- Remember 'number' does not coerce strings — only 'financial' does (and only legacy)
- Add a lint/test that formatted fields come from typed numeric columns
When it happens
Trigger: Calling format(value, 'number') with a string like '123' (common after fetching data that wasn't coerced), null/undefined, NaN-adjacent values from a spreadsheet cell, or an object returned by a query aggregation.
Common situations: Binding a report/CSV column configured as 'number' to data that arrives as strings from an API or AQL query; passing state from an uncontrolled input (always a string) directly to the formatter; a refactor changing the data type upstream.
Related errors
- Value is not a number (${typeof localValue}): ${localValue}
- Invalid config file: key "${key}" must be a string, got ${ty
- Invalid --name: must be a non-empty string.
- No update fields provided. Use --name or --offbudget.
- Invalid cutoff date: expected a valid date (e.g. YYYY-MM-DD)
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b97720accca9b37f.
Report an issue: GitHub.