transact-rs/sqlx · error · io::Error (InvalidData)

Reading a `MONEY` value in text format is not supported.

Error message

Reading a `MONEY` value in text format is not supported.

What it means

Postgres MONEY values in text format include locale-dependent formatting such as currency symbols and thousands separators, which sqlx cannot safely parse. Therefore PgMoney's Decode impl only supports the binary wire format and deliberately rejects text format with this error. The library chooses to fail explicitly rather than guess at parsing localized text.

Source

Thrown at sqlx-postgres/src/types/money.rs:184

impl Encode<'_, Postgres> for PgMoney {
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
        buf.extend(&self.0.to_be_bytes());

        Ok(IsNull::No)
    }
}

impl Decode<'_, Postgres> for PgMoney {
    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
        match value.format() {
            PgValueFormat::Binary => {
                let cents = BigEndian::read_i64(value.as_bytes()?);

                Ok(PgMoney(cents))
            }
            PgValueFormat::Text => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidData,
                    "Reading a `MONEY` value in text format is not supported.",
                );

                Err(Box::new(error))
            }
        }
    }
}

impl Add<PgMoney> for PgMoney {
    type Output = PgMoney;

    /// Adds two monetary values.
    ///
    /// # Panics
    /// Panics if overflowing the `i64::MAX`.
    fn add(self, rhs: PgMoney) -> Self::Output {

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Use a regular prepared query (`sqlx::query(...).fetch_...`) so the binary format is used
  2. Cast the column in SQL to a numeric type and decode to i64/f64/BigDecimal: `SELECT amount::numeric FROM t`
  3. Change the column type to NUMERIC/BIGINT (cents) which both formats support
  4. If you must read text, fetch as String and strip currency symbols/separators manually before constructing PgMoney(cents)

Example fix

// before
let row = sqlx::query("SELECT amount FROM payments").fetch_one(&db).await?; // simple_query/text path
let money: PgMoney = row.get("amount");
// after: use prepared query or cast
let money: PgMoney = sqlx::query_scalar("SELECT amount FROM payments").fetch_one(&db).await?;
// or
let cents: i64 = sqlx::query_scalar("SELECT amount::numeric::bigint FROM payments").fetch_one(&db).await?;
Defensive patterns

Strategy: try-catch

Try / catch

// decode MONEY defensively, falling back to a numeric cast
let money: PgMoney = match sqlx::query_scalar("SELECT amount FROM payments").fetch_one(&db).await {
    Ok(m) => m,
    Err(e) if e.to_string().contains("text format is not supported") => {
        let cents: i64 = sqlx::query_scalar("SELECT amount::numeric::bigint FROM payments").fetch_one(&db).await?;
        PgMoney(cents)
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Fetching a MONEY column as PgMoney when the query executes over the text protocol — e.g. a plain (unprepared) query via `sqlx::query` with simple_query, or a connection/driver configuration forcing text format.

Common situations: Using `simple_query` for quick reads of MONEY columns; running through tools or pools that force text format; selecting `money::text` and still binding to PgMoney; migrations/replication tools that emit text rows.

Related errors


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