windmill-labs/windmill · error · ConversionError::Io (InvalidInput)
unknown replication message tag `{}`
Error message
unknown replication message tag `{}` What it means
The top-level dispatcher inside LogicalReplicationMessage::parse only recognizes the pgoutput logical message tags: 'B' (Begin), 'C' (Commit), 'R' (Relation), 'Y' (Type), 'I' (Insert), 'U' (Update), 'D' (Delete). This error fires when the first byte of the logical message payload is none of those, meaning the XLogData body is not a logical-decoding message the library understands. Decoding aborts with an InvalidInput io::Error wrapped in ConversionError.
Source
Thrown at backend/windmill-trigger-postgres/src/replication_message.rs:458
TUPLE_OLD_BYTE => old_tuple = Some(TupleData::parse(&mut buf)?),
TUPLE_KEY_BYTE => key_tuple = Some(TupleData::parse(&mut buf)?),
tag => {
return Err(ConversionError::Io(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unknown tuple tag `{}`", tag),
)));
}
}
LogicalReplicationMessage::Delete(DeleteBody::new(
transaction_id,
o_id,
old_tuple,
key_tuple,
))
}
byte => {
return Err(ConversionError::Io(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unknown replication message tag `{}`", byte),
)));
}
};
Ok(logical_replication_message)
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ReplicationMessage {
XLogData(XLogDataBody),
PrimaryKeepAlive(PrimaryKeepAliveBody),
}
impl ReplicationMessage {View on GitHub (pinned to e474e8803c)
Solutions
- Check which pgoutput protocol version the slot was created with; drop and recreate the slot with a protocol version/options the parser supports (no streamed or two-phase transactions).
- Disable options that emit unsupported message tags: avoid pg_logical_emit_message usage and REORDER/WRITE in binary mode until the parser handles those tags.
- Verify you only feed the XLogData payload (the inner data of 'w' CopyData frames) to LogicalReplicationMessage::parse, not the outer ReplicationMessage bytes.
- Look at the reported byte and cross-check it against the PostgreSQL logical replication message docs to identify what message type your server is sending.
- If the byte is a valid newer pgoutput tag (e.g. 'M', 'S'), extend the match in replication_message.rs to skip or decode it.
Defensive patterns
Strategy: validation
Validate before calling
// reject unsupported pgoutput options before opening the slot
fn validate_slot_options(protocol_version: i32, streaming: bool, two_phase: bool) -> Result<(), String> {
if protocol_version > 1 {
return Err("protocol_version >1 emits streaming tags this parser does not know".into());
}
if streaming || two_phase {
return Err("streamed/two-phase transactions emit 'S'/'E'/'c' tags unsupported by the parser".into());
}
Ok(())
} Type guard
fn is_known_logical_tag(payload: &[u8]) -> bool {
matches!(payload.first(), Some(b'B' | b'C' | b'R' | b'Y' | b'I' | b'U' | b'D'))
} Try / catch
match LogicalReplicationMessage::parse(buf, settings) {
Ok(msg) => handle(msg),
Err(e) if e.to_string().contains("unknown replication message tag") => {
let tag = /* extract from message */;
log::warn!("unsupported pgoutput tag {tag:?}; check slot protocol options");
}
Err(e) => return Err(e.into()),
} Prevention
- Create replication slots with the lowest protocol version the parser supports (START_REPLICATION ... (proto_version '1', publication_names '...'))
- Do not enable streaming, two-phase, or messages options unless the parser implements those tags
- Verify you pass only XLogData inner payloads to LogicalReplicationMessage::parse, never outer CopyData bytes
- Check pg_replication_slots for plugin='pgoutput' and avoid custom output plugins with this parser
When it happens
Trigger: Calling LogicalReplicationMessage::parse with a buffer whose first byte is not one of B/C/R/Y/I/U/D — e.g. passing a raw COPY-both protocol frame, a keepalive, or an unknown logical message tag such as 'M' (logical decoding message) or 'S' (streaming start/stop) that the parser does not implement.
Common situations: Enabling protocol options the parser lacks support for — e.g. protocol_version >= 2/3 with streamed (two-phase) transactions emitting 'S'/'s'/'E'/'c' streaming tags, or pg_logical_emit_message producing 'M' messages; pointing the parser at non-XLogData bytes; truncation shifting the stream into message bodies; PostgreSQL version emitting newer tag types.
Related errors
- unknown tuple byte `{}`
- unknown tuple tag `{}`
- unknown replication message byte `{}`
- unexpected EOF
- unknown replica identity byte `{}`
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/c0da1d5a44d59d1b.
Report an issue: GitHub.