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

VARBIT length mismatch.

Error message

VARBIT length mismatch.

What it means

Postgres VARBIT/BIT values are decoded by reading the declared bit length and computing the expected byte count (`len.div_ceil(8)`). If the wire buffer's remaining bytes don't exactly match that count, the payload is malformed, and decoding fails with `InvalidData` and 'VARBIT length mismatch.'.

Source

Thrown at sqlx-postgres/src/types/bit_vec.rs:61

    fn size_hint(&self) -> usize {
        mem::size_of::<i32>() + self.len()
    }
}

impl Decode<'_, Postgres> for BitVec {
    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
        match value.format() {
            PgValueFormat::Binary => {
                let mut bytes = value.as_bytes()?;
                let len = bytes.get_i32();

                let len = usize::try_from(len).map_err(|_| format!("invalid VARBIT len: {len}"))?;

                // The smallest amount of data we can read is one byte
                let bytes_len = len.div_ceil(8);

                if bytes.remaining() != bytes_len {
                    Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "VARBIT length mismatch.",
                    ))?;
                }

                let mut bitvec = BitVec::from_bytes(bytes);

                // Chop off zeroes from the back. We get bits in bytes, so if
                // our bitvec is not in full bytes, extra zeroes are added to
                // the end.
                while bitvec.len() > len {
                    bitvec.pop();
                }

                Ok(bitvec)
            }
            PgValueFormat::Text => {
                let s = value.as_str()?;

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Verify the column type and data with psql (`SELECT bit_length(col), col FROM ...`) to confirm server-side integrity
  2. Re-insert or recompute the affected BIT/VARBIT values if data is corrupted
  3. Remove/fix any proxy or custom protocol layer mangling the BIT payload
  4. If you control encoding, ensure the payload is exactly `ceil(len/8)` bytes for the declared bit length

Example fix

// before: fixture with mismatched declared length vs bytes
let value: BitVec = sqlx::decode::decode(&[0u8, 9, 0xFF].encode()[..])?; // len=9 but 1 byte only? mismatch cases panic here
// after: encode with matching length prefix
let bits = BitVec::from_slice(&[0b1111_1111, 0b1000_0000]);
let encoded = PgBitVec::encode(bits); // consistent len/bytes
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check length consistency before decoding raw bytes
let len = u32::from_be_bytes(raw[0..4].try_into().unwrap()) as usize;
assert_eq!(raw.len() - 4, len.div_ceil(8), "BIT payload length mismatch");

Try / catch

match row.try_get::<BitVec, _>("flags") {
    Ok(bits) => bits,
    Err(e) if e.to_string().contains("VARBIT length mismatch") => {
        // log and inspect raw column data; treat row as corrupt
        BitVec::new()
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Decoding a BIT/VARBIT column whose received payload length disagrees with its declared bit length — corrupted/truncated result data, or a custom/proxy server emitting a non-conforming wire format.

Common situations: Bit column written by another tool with an inconsistent length prefix; values produced through an intermediate server or driver with a wire-format bug; manually hand-crafted test fixtures with wrong byte counts.

Related errors


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