nautechsystems/nautilus_trader · error

Stream message missing payload: {stream_msg:?}

Error message

Stream message missing payload: {stream_msg:?}

What it means

A Redis stream entry decoded by decode_bus_message must include a 'payload' header with the message body. When the entry parsed successfully but no 'payload' pair was present, the decoder bails because a BusMessage without a body is meaningless. It signals an entry that does not conform to the message-bus wire format.

Source

Thrown at crates/infrastructure/src/redis/msgbus.rs:882

                encoding = value
                    .parse()
                    .map_err(|e| anyhow::anyhow!("Error parsing encoding: {e}"))?;
            }
            b"payload" => {
                let redis::Value::BulkString(bytes) = &pair[1] else {
                    anyhow::bail!("Invalid payload format: {stream_msg:?}");
                };
                payload = Some(Bytes::copy_from_slice(bytes));
            }
            _ => {}
        }
    }

    let Some(topic) = topic else {
        anyhow::bail!("Stream message missing topic: {stream_msg:?}");
    };
    let Some(payload) = payload else {
        anyhow::bail!("Stream message missing payload: {stream_msg:?}");
    };
    let payload_type = match type_name {
        Some(type_name) if typed_payload => BusPayloadType::from_typed_name(&type_name)
            .ok_or_else(|| anyhow::anyhow!("Unknown typed payload '{type_name}'"))?,
        Some(type_name) => BusPayloadType::from_name(&type_name),
        None if typed_payload => {
            anyhow::bail!("Typed stream message missing type: {stream_msg:?}")
        }
        None => BusPayloadType::Custom(Ustr::default()),
    };

    Ok(BusMessage::with_str_topic(
        topic,
        payload_type,
        payload,
        encoding,
    ))
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the entry (XRANGE) and verify whether it legitimately lacks a payload; if it is a control entry, exclude that stream from the bus consumer.
  2. Fix the producer so every published message includes a 'payload' BulkString.
  3. Skip or XDEL the payload-less entry so the consumer can continue.
  4. Verify the stream key/topic the consumer is reading matches what the producer publishes.

Example fix

// before (producer bug omits payload)
XADD mystream * topic "quotes.X" encoding "json"
// after
XADD mystream * topic "quotes.X" payload "<body>" encoding "json"
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure payload present before publishing
fn entry_has_payload(fields: &[(&str, &[u8])]) -> bool {
    fields.iter().any(|(k, _)| *k == "payload")
}

Type guard

fn payload_of(fields: &[(String, redis::Value)]) -> Option<&Vec<u8>> {
    fields.iter().find(|(k, _)| k == "payload")
        .and_then(|(_, v)| match v { redis::Value::BulkString(b) => Some(b), _ => None })
}

Try / catch

match stream_messages(&mut con, stream, count).await {
    Ok(msgs) => { /* use msgs */ }
    Err(e) if e.to_string().contains("missing payload") => {
        tracing::warn!("skipping payload-less entry: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: stream_messages encounters an entry with headers such as topic/type/encoding but no 'payload' field — e.g. a heartbeat or control entry written by a different component, a producer bug that drops the payload, or a foreign entry in the stream.

Common situations: Stream key shared with control/heartbeat entries from other tooling; producer version skew; manual redis-cli XADD during testing; consumer attached to the wrong stream.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/d81d592d2966172b. Report an issue: GitHub.