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_ARRAY

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Validate/clamp the BigDecimal range before conversion (check magnitude against i64::MAX / multiplier)
  2. Store large amounts as NUMERIC instead of MONEY and map to BigDecimal in sqlx
  3. Normalize the decimal's scale first (with_scale) so the multiplier and result stay in range
  4. 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

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


AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03). Data as JSON: /api/errors/af732acdbb68c609. Report an issue: GitHub.