nautechsystems/nautilus_trader · error

decoded {} stream payload contains reserved payload_type fie

Error message

decoded {} stream payload contains reserved payload_type field

What it means

When decoding an external msgbus stream payload from JSON, the decoder builds an object and injects its own 'payload_type' field to route the message. If the decoded payload already contains a 'payload_type' key, inserting would overwrite or conflict with the reserved key, so the library deliberately rejects it with this error to keep the envelope schema unambiguous.

Source

Thrown at crates/common/src/msgbus/external/mod.rs:444

        message.encoding,
        &message.payload,
    )?
    else {
        return Ok(());
    };
    let mut mapping = serde_json::to_value(&value).with_context(|| {
        format!(
            "failed to map decoded {} stream payload",
            message.payload_type
        )
    })?;
    let mapping_object = mapping.as_object_mut().with_context(|| {
        format!(
            "decoded {} stream payload did not map to an object",
            message.payload_type
        )
    })?;
    anyhow::ensure!(
        !mapping_object.contains_key("payload_type"),
        "decoded {} stream payload contains reserved payload_type field",
        message.payload_type
    );
    mapping_object.insert(
        "payload_type".to_string(),
        serde_json::Value::String(message.payload_type.as_str().to_string()),
    );

    processor(&value, &mapping)?;
    if is_registered_streaming_type(message) {
        publish_any(topic, &value);
    }
    Ok(())
}

fn is_registered_streaming_type(message: &BusMessage) -> bool {
    if get_message_bus()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Remove 'payload_type' from the payload body before publishing; let the msgbus envelope add it during decoding
  2. If you control the publisher, rename the field to something like 'msg_type' or nest the data under a sub-key
  3. When replaying captured traffic, strip envelope-level keys ('payload_type', 'topic', etc.) from the recorded body first
  4. Sanitize incoming payloads in your bridge/adapter with a pre-publish filter that deletes reserved keys

Example fix

// before
payload = {"payload_type": "Data", "data": record}
msgbus.publish(topic, payload)
// after
payload = {"data": record}  # payload_type is injected by the decoder envelope
msgbus.publish(topic, payload)
Defensive patterns

Strategy: validation

Validate before calling

def assert_no_reserved_keys(payload: dict) -> None:
    reserved = {"payload_type"}
    clash = reserved & payload.keys()
    if clash:
        raise ValueError(f"payload uses reserved keys: {clash}")

# call before publishing to the external msgbus stream
assert_no_reserved_keys(payload)

Type guard

def is_safe_payload(payload: dict) -> bool:
    return isinstance(payload, dict) and "payload_type" not in payload

Prevention

When it happens

Trigger: Publishing or forwarding a message whose serialized JSON body includes a top-level 'payload_type' key into the external message bus stream decoder (process_typed_payload), so the decoded object collides with the envelope's reserved routing field.

Common situations: Producers that already stamp their own 'payload_type' field into payloads (e.g. mirroring Nautilus envelope format in custom publishers, replaying recorded messages that include envelope fields, or bridging another bus whose schema uses the same key).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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