windmill-labs/windmill · error · ConversionError::Io (InvalidInput)
unknown replication message byte `{}`
Error message
unknown replication message byte `{}` What it means
The Postgres logical-replication message parser dispatches on a leading byte identifying the message/tuple-data type. When it encounters a byte it has no match arm for, it raises InvalidInput 'unknown replication message byte `{}`'. This indicates the stream contains a message type this parser doesn't implement (or the stream is misaligned/corrupt).
Source
Thrown at backend/windmill-trigger-postgres/src/replication_message.rs:192
for _ in 0..number_of_columns {
let byte = buf.read_u8()?;
let tuple_data = match byte {
TUPLE_DATA_NULL_BYTE => TupleData::Null,
TUPLE_DATA_TOAST_BYTE => TupleData::UnchangedToast,
TUPLE_DATA_TEXT_BYTE => {
let len = buf.read_i32::<BigEndian>()?;
let mut data = vec![0; len as usize];
buf.read_exact(&mut data)?;
TupleData::Text(data.into())
}
TUPLE_DATA_BINARY_BYTE => {
let len = buf.read_i32::<BigEndian>()?;
let mut data = vec![0; len as usize];
buf.read_exact(&mut data)?;
TupleData::Binary(data.into())
}
byte => {
return Err(ConversionError::Io(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unknown replication message byte `{}`", byte),
)));
}
};
tuples.push(tuple_data);
}
Ok(tuples)
}
}
#[derive(Debug)]
pub enum TransactionBody {
Insert(InsertBody),
Update(UpdateBody),
Delete(DeleteBody),View on GitHub (pinned to e474e8803c)
Solutions
- Check which byte value was reported and compare against the pgoutput protocol to identify the unhandled message type.
- Upgrade the windmill-trigger-postgres parser (or Windmill itself) to a version supporting that message type.
- Drop and recreate the replication slot/publication so the stream restarts aligned.
- Verify wal_level=logical and the pgoutput protocol options match what the parser expects.
Defensive patterns
Strategy: try-catch
Try / catch
match parse(msg_bytes) {
Err(ConversionError::Io(e)) if e.to_string().contains("unknown replication message byte") => {
tracing::warn!("unsupported pgoutput message: {e}; skipping and resyncing slot");
// recreate slot or upgrade parser
}
Ok(msg) => handle(msg),
Err(e) => return Err(e.into()),
} Prevention
- Keep Windmill's postgres trigger code in sync with your Postgres server version
- Verify wal_level=logical and pgoutput protocol options before enabling replication
- Recreate replication slots after Postgres major upgrades
- Log unknown bytes with context to identify unsupported message types quickly
When it happens
Trigger: Postgres sends a logical replication message type (or TupleData sub-message byte) not covered by the parser's match — e.g. a newer Postgres version emitting unsupported message kinds, or the buffer being read at a wrong offset after a partial/failed parse.
Common situations: Upgrading the Postgres server to a version emitting new replication message types; parsing a pgoutput stream with a plugin/format mismatch; corrupted or misaligned replication slot stream after a previous parse error.
Related errors
- unexpected EOF
- unknown replica identity byte `{}`
- unexpected tuple byte `{}`
- unknown tuple byte `{}`
- unknown tuple tag `{}`
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/559b8ce4b4d416dc.
Report an issue: GitHub.