transact-rs/sqlx · error · io::Error (InvalidData)
Provided BigDecimal could not convert to i64: overflow.
Error message
Provided BigDecimal could not convert to i64: overflow.
What it means
PgMoney::from_bigdecimal converts a BigDecimal amount into cents by multiplying by a multiplier and fitting the result into an i64. If the scaled value exceeds i64 range, the to_i64() conversion returns None and sqlx raises this InvalidData error instead of silently truncating. It protects against storing a money value that cannot be represented in Postgres MONEY (8-byte cents).
Source
Thrown at sqlx-postgres/src/types/money.rs:136
/// Convert a [`BigDecimal`](bigdecimal::BigDecimal) value into money using the correct precision
/// defined in the PostgreSQL settings. The default precision is two.
#[cfg(feature = "bigdecimal")]
pub fn from_bigdecimal(
decimal: bigdecimal::BigDecimal,
locale_frac_digits: u32,
) -> Result<Self, BoxDynError> {
use bigdecimal::ToPrimitive;
let multiplier = bigdecimal::BigDecimal::new(
num_bigint::BigInt::from(10i128.pow(locale_frac_digits)),
0,
);
let cents = decimal * multiplier;
let money = cents.to_i64().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"Provided BigDecimal could not convert to i64: overflow.",
)
})?;
Ok(Self(money))
}
}
impl Type<Postgres> for PgMoney {
fn type_info() -> PgTypeInfo {
PgTypeInfo::MONEY
}
}
impl PgHasArrayType for PgMoney {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::MONEY_ARRAYView on GitHub (pinned to 03af8bcc57)
Solutions
- Validate/clamp the BigDecimal range before conversion (check magnitude against i64::MAX / multiplier)
- Store large amounts as NUMERIC instead of MONEY and map to BigDecimal in sqlx
- Normalize the decimal's scale first (with_scale) so the multiplier and result stay in range
- Reject or log amounts above your domain's real-world maximum at input validation time
Example fix
// before
let money = PgMoney::from_bigdecimal(amount, 2)?;
// after: bound-check first
let cents = &amount * 100;
if cents > BigDecimal::from(i64::MAX) || cents < BigDecimal::from(i64::MIN) {
return Err(anyhow!("amount {} out of MONEY range", amount));
}
let money = PgMoney::from_bigdecimal(amount, 2)?; Defensive patterns
Strategy: validation
Validate before calling
use bigdecimal::BigDecimal;
fn fits_money(amount: &BigDecimal, multiplier: i64) -> bool {
let cents = amount * BigDecimal::from(multiplier);
cents <= BigDecimal::from(i64::MAX) && cents >= BigDecimal::from(i64::MIN)
} Type guard
fn to_i64_cents(d: &bigdecimal::BigDecimal) -> Option<i64> {
use bigdecimal::ToPrimitive;
d.to_i64()
} Try / catch
match PgMoney::from_bigdecimal(amount.clone(), 2) {
Ok(money) => store(money),
Err(e) if e.to_string().contains("could not convert to i64") => {
// store as NUMERIC instead of MONEY
store_numeric(&amount)?;
}
Err(e) => return Err(e.into()),
} Prevention
- Bound-check monetary inputs at the application boundary (max transaction amount)
- Store amounts as NUMERIC rather than MONEY when values can grow via aggregation
- Unit-test conversions with boundary values (i64::MAX/100, i64::MIN/100)
When it happens
Trigger: Calling PgMoney::from_bigdecimal with a BigDecimal whose absolute value, after multiplication by the currency multiplier, overflows i64 (roughly > 92 quadrillion cents or extremely small scales with huge multipliers).
Common situations: Loading user-supplied or aggregated amounts (sums of many transactions) without bounds; passing a BigDecimal with absurd exponent/scale parsed from untrusted input; currency conversion multiplying values past i64 range.
Related errors
- Reading a `MONEY` value in text format is not supported.
- VARBIT length mismatch.
- VARBIT data contains other characters than 1 or 0.
- cleanup_test() invoked outside `#[sqlx::test]`
- DATABASE_URL must be set
AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03).
Data as JSON: /api/errors/af732acdbb68c609.
Report an issue: GitHub.