Automattic/mongoose · error · CastError
Cast to Decimal128 failed for value "${value}" (type ${value
Error message
Cast to Decimal128 failed for value "${value}" (type ${valueType}) at path "${path}" What it means
SchemaDecimal128.cast converts failures of the Decimal128 caster (lib/cast/decimal128.js) into this CastError. The caster accepts Decimal128 instances, strings via Decimal128.fromString (strict decimal-literal parsing), numbers, `{ $numberDecimal: '...' }` Extended JSON, and 16-byte Buffer/Uint8Array values. It rejects booleans, plain objects, arrays, and strings Decimal128.fromString cannot parse ('abc', '1,5', '$10.00').
Source
Thrown at lib/schema/decimal128.js:206
return value;
}
return this._castRef(value, doc, init, options);
}
let castDecimal128;
if (typeof this._castFunction === 'function') {
castDecimal128 = this._castFunction;
} else if (typeof this.constructor.cast === 'function') {
castDecimal128 = this.constructor.cast();
} else {
castDecimal128 = SchemaDecimal128.cast();
}
try {
return castDecimal128(value);
} catch (error) {
throw new CastError('Decimal128', value, this.path, error, this);
}
};
/*!
* ignore
*/
function handleSingle(val) {
return this.cast(val);
}
const $conditionalHandlers = {
...SchemaType.prototype.$conditionalHandlers,
$gt: handleSingle,
$gte: handleSingle,
$lt: handleSingle,
$lte: handleSingle
};View on GitHub (pinned to 49cdab0136)
Solutions
- Sanitize to a plain decimal string before assigning: strip currency symbols/thousands separators, then validate against `/^[+-]?\d+(\.\d+)?$/`
- Assign `mongoose.Types.Decimal128.fromString(cleanValue)` yourself inside a try/catch at the boundary
- Pass numbers directly (`doc.price = 10.5`) when precision loss is acceptable
- If input formats vary wildly, register a custom caster with `mongoose.Schema.Types.Decimal128.cast(...)`
Example fix
// before
product.price = req.body.price; // '$1,234.56'
// after
const clean = String(req.body.price).replace(/[$,]/g, '');
if (!/^[+-]?\d+(\.\d+)?$/.test(clean)) throw new ValidationError('bad price');
product.price = mongoose.Types.Decimal128.fromString(clean); Defensive patterns
Strategy: validation
Validate before calling
const DECIMAL_RE = /^[+-]?\d+(\.\d+)?$/;
function toDecimal128(v) {
if (v == null) return v;
const s = typeof v === 'object' && typeof v.$numberDecimal === 'string' ? v.$numberDecimal : String(v);
const clean = s.replace(/[$,\s]/g, '');
if (!DECIMAL_RE.test(clean)) throw new Error(`not a decimal: ${s}`);
return mongoose.Types.Decimal128.fromString(clean);
} Type guard
function isDecimalLike(v) {
if (v == null || typeof v === 'number') return true;
const s = typeof v === 'string' ? v : (v && v.$numberDecimal);
return typeof s === 'string' && /^[+-]?\d+(\.\d+)?$/.test(s.replace(/[$,]/g, ''));
} Try / catch
try { doc.price = value; } catch (err) { if (err.name === 'CastError' && err.kind === 'Decimal128') { return badRequest('price must be a decimal number'); } throw err; } Prevention
- Sanitize currency/locale strings before they reach models
- Validate decimal fields with a strict regex in your request schema
- Assign Decimal128.fromString() yourself at the boundary to control the error
When it happens
Trigger: `doc.price = 'abc'`, `doc.price = { value: '1.5' }` (object without $numberDecimal), `doc.price = '1.234,56'` (comma decimal), `doc.price = '$10.00'` (currency symbol), or passing an array. Query side: `{ price: { $gt: 'free' } }` via handleSingle.
Common situations: Money fields fed unsanitized user input; locale-formatted numbers from European formats; currency strings from scraping or spreadsheets; JSON where the decimal arrives inside a wrapper object from another serializer.
Related errors
- Query filter must be an object, got an array ${util.inspect(
- Cast to date failed for value "${value}" (type ${valueType})
- Cast to Double failed for value "${value}" (type ${valueType
- Cast to Int32 failed for value "${value}" (type ${valueType}
- Cast to Number failed for value "${value}" (type ${valueType
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/f12ee55a585daced.
Report an issue: GitHub.