{"record":{"id":"e5e0e4077e0d1ff7","repo":"nautechsystems/nautilus_trader","slug":"decoded-stream-payload-contains-reserved-payloa","errorCode":null,"errorMessage":"decoded {} stream payload contains reserved payload_type field","messagePattern":"decoded (.+?) stream payload contains reserved payload_type field","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/msgbus/external/mod.rs","lineNumber":444,"sourceCode":"        message.encoding,\n        &message.payload,\n    )?\n    else {\n        return Ok(());\n    };\n    let mut mapping = serde_json::to_value(&value).with_context(|| {\n        format!(\n            \"failed to map decoded {} stream payload\",\n            message.payload_type\n        )\n    })?;\n    let mapping_object = mapping.as_object_mut().with_context(|| {\n        format!(\n            \"decoded {} stream payload did not map to an object\",\n            message.payload_type\n        )\n    })?;\n    anyhow::ensure!(\n        !mapping_object.contains_key(\"payload_type\"),\n        \"decoded {} stream payload contains reserved payload_type field\",\n        message.payload_type\n    );\n    mapping_object.insert(\n        \"payload_type\".to_string(),\n        serde_json::Value::String(message.payload_type.as_str().to_string()),\n    );\n\n    processor(&value, &mapping)?;\n    if is_registered_streaming_type(message) {\n        publish_any(topic, &value);\n    }\n    Ok(())\n}\n\nfn is_registered_streaming_type(message: &BusMessage) -> bool {\n    if get_message_bus()","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/msgbus/external/mod.rs#L426-L462","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Remove 'payload_type' from the payload body before publishing; let the msgbus envelope add it during decoding","If you control the publisher, rename the field to something like 'msg_type' or nest the data under a sub-key","When replaying captured traffic, strip envelope-level keys ('payload_type', 'topic', etc.) from the recorded body first","Sanitize incoming payloads in your bridge/adapter with a pre-publish filter that deletes reserved keys"],"exampleFix":"// before\npayload = {\"payload_type\": \"Data\", \"data\": record}\nmsgbus.publish(topic, payload)\n// after\npayload = {\"data\": record}  # payload_type is injected by the decoder envelope\nmsgbus.publish(topic, payload)","handlingStrategy":"validation","validationCode":"def assert_no_reserved_keys(payload: dict) -> None:\n    reserved = {\"payload_type\"}\n    clash = reserved & payload.keys()\n    if clash:\n        raise ValueError(f\"payload uses reserved keys: {clash}\")\n\n# call before publishing to the external msgbus stream\nassert_no_reserved_keys(payload)","typeGuard":"def is_safe_payload(payload: dict) -> bool:\n    return isinstance(payload, dict) and \"payload_type\" not in payload","tryCatchPattern":null,"preventionTips":["Never embed envelope-style keys (payload_type, topic) inside payload bodies","Strip/rename producer-side fields that collide with the msgbus envelope schema","Add a publish-side lint/assert in adapters that forward third-party messages","Document the reserved key set for your team's custom publishers"],"tags":["serialization","msgbus","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}