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
- Use a regular prepared query (`sqlx::query(...).fetch_...`) so the binary format is used
- Cast the column in SQL to a numeric type and decode to i64/f64/BigDecimal: `SELECT amount::numeric FROM t`
- Change the column type to NUMERIC/BIGINT (cents) which both formats support
- 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
- Never read MONEY columns via simple_query or other text-format paths
- Prefer NUMERIC/BIGINT (cents) column types over Postgres MONEY
- Cast in SQL (::numeric) when interop with text-based tooling is required
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
- VARBIT length mismatch.
- VARBIT data contains other characters than 1 or 0.
- Provided BigDecimal could not convert to i64: overflow.
- 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/9060d951a38c337a.
Report an issue: GitHub.