risingwavelabs/risingwave · error

The proto payload is empty

Error message

The proto payload is empty

What it means

`resolve_pb_header` uses the encoded proto header to compute field-index offsets (Confluent wire format navigation). If header resolution yields no remaining payload bytes, there is nothing to decode, so the function bails. This indicates a malformed or empty protobuf message body rather than a schema problem.

Source

Thrown at src/connector/src/parser/protobuf/parser.rs:222

/// | 0          | 1-4        | 5-x             | x+1-end
/// | magic-byte | schema-id  | message-indexes | protobuf-payload
pub(crate) fn resolve_pb_header(payload: &[u8]) -> ConnectorResult<&[u8]> {
    // there's a message index array at the front of payload
    // if it is the first message in proto def, the array is just and `0`
    let (_, remained) = extract_schema_id(payload)?;
    // The message indexes are encoded as int using variable-length zig-zag encoding,
    // prefixed by the length of the array.
    // Note that if the first byte is 0, it is equivalent to (1, 0) as an optimization.
    match remained.first() {
        Some(0) => Ok(&remained[1..]),
        Some(_) => {
            let (index_len, mut offset) = decode_varint_zigzag(remained)?;
            for _ in 0..index_len {
                offset += decode_varint_zigzag(&remained[offset..])?.1;
            }
            Ok(&remained[offset..])
        }
        None => bail!("The proto payload is empty"),
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_decode_varint_zigzag() {
        // 1. Positive number
        let buffer = vec![0x02];
        let (value, len) = decode_varint_zigzag(&buffer).unwrap();
        assert_eq!(value, 1);
        assert_eq!(len, 1);

        // 2. Negative number
        let buffer = vec![0x01];
        let (value, len) = decode_varint_zigzag(&buffer).unwrap();

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Filter out empty/tombstone messages upstream or enable tombstone handling so empty payloads are skipped.
  2. Verify the producer is publishing valid non-empty protobuf bytes.
  3. Check you are reading the correct topic/partition with actual protobuf-encoded records.
  4. Add a payload-size/type check in the producing application before send.

Example fix

// before: producer sends empty payload
producer.send(record_with(b""));
// after: skip tombstones
if payload.is_empty() { return Ok(()); }
Defensive patterns

Strategy: try-catch

Validate before calling

// Producer-side guard before publishing:
if payload.is_empty() { return Err("refusing to publish empty protobuf payload"); }

Try / catch

match decode_record(bytes) {
    Err(e) if e.to_string().contains("The proto payload is empty") => {
        // treat as tombstone: skip or emit a delete marker
        Ok(None)
    }
    other => other,
}

Prevention

When it happens

Trigger: Called from `generate_accessor` while building the protobuf accessor; fires when the remaining payload slice after varint index navigation is `None` — i.e. the message bytes are empty (zero-length Kafka value) or nothing remains after the header.

Common situations: Kafka topic contains tombstone records (null/empty values) with protobuf encoding; a producer publishing empty payloads by misconfiguration; compacted-topic cleanup sending empty bodies; reading the wrong topic where records are not protobuf-encoded.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/657cfa32ce35ffc8. Report an issue: GitHub.