{"record":{"id":"c0da1d5a44d59d1b","repo":"windmill-labs/windmill","slug":"unknown-replication-message-tag","errorCode":null,"errorMessage":"unknown replication message tag `{}`","messagePattern":"unknown replication message tag `(.+?)`","errorType":"exception","errorClass":"ConversionError::Io (InvalidInput)","httpStatus":null,"severity":"error","filePath":"backend/windmill-trigger-postgres/src/replication_message.rs","lineNumber":458,"sourceCode":"                    TUPLE_OLD_BYTE => old_tuple = Some(TupleData::parse(&mut buf)?),\n                    TUPLE_KEY_BYTE => key_tuple = Some(TupleData::parse(&mut buf)?),\n                    tag => {\n                        return Err(ConversionError::Io(io::Error::new(\n                            io::ErrorKind::InvalidInput,\n                            format!(\"unknown tuple tag `{}`\", tag),\n                        )));\n                    }\n                }\n\n                LogicalReplicationMessage::Delete(DeleteBody::new(\n                    transaction_id,\n                    o_id,\n                    old_tuple,\n                    key_tuple,\n                ))\n            }\n            byte => {\n                return Err(ConversionError::Io(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"unknown replication message tag `{}`\", byte),\n                )));\n            }\n        };\n\n        Ok(logical_replication_message)\n    }\n}\n\n#[non_exhaustive]\n#[derive(Debug)]\npub enum ReplicationMessage {\n    XLogData(XLogDataBody),\n    PrimaryKeepAlive(PrimaryKeepAliveBody),\n}\n\nimpl ReplicationMessage {","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-trigger-postgres/src/replication_message.rs#L440-L476","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// reject unsupported pgoutput options before opening the slot\nfn validate_slot_options(protocol_version: i32, streaming: bool, two_phase: bool) -> Result<(), String> {\n    if protocol_version > 1 {\n        return Err(\"protocol_version >1 emits streaming tags this parser does not know\".into());\n    }\n    if streaming || two_phase {\n        return Err(\"streamed/two-phase transactions emit 'S'/'E'/'c' tags unsupported by the parser\".into());\n    }\n    Ok(())\n}","typeGuard":"fn is_known_logical_tag(payload: &[u8]) -> bool {\n    matches!(payload.first(), Some(b'B' | b'C' | b'R' | b'Y' | b'I' | b'U' | b'D'))\n}","tryCatchPattern":"match LogicalReplicationMessage::parse(buf, settings) {\n    Ok(msg) => handle(msg),\n    Err(e) if e.to_string().contains(\"unknown replication message tag\") => {\n        let tag = /* extract from message */;\n        log::warn!(\"unsupported pgoutput tag {tag:?}; check slot protocol options\");\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["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"],"tags":["postgres","replication","pgoutput","protocol-decoding"],"backgroundTag":"replication-protocol-parse-error","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}