{"record":{"id":"14f6f7d601ed76c6","repo":"nautechsystems/nautilus_trader","slug":"customdata-envelope-type-envelope-type-name-do","errorCode":null,"errorMessage":"CustomData envelope type '{envelope_type_name}' does not match message type '{custom_type_name}'","messagePattern":"CustomData envelope type '(.+?)' does not match message type '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/msgbus/external/mod.rs","lineNumber":585,"sourceCode":"    }\n}\n\nfn decode_custom_data_value(\n    custom_type_name: Ustr,\n    value: &serde_json::Value,\n) -> anyhow::Result<Option<CustomData>> {\n    let Some(data) = deserialize_custom_from_json(custom_type_name.as_str(), value)? else {\n        log::warn!(\n            \"External custom payload type '{custom_type_name}' is not registered for inbound republishing\"\n        );\n        return Ok(None);\n    };\n\n    let envelope_type_name = value\n        .get(\"type\")\n        .and_then(serde_json::Value::as_str)\n        .context(\"CustomData JSON missing 'type' field\")?;\n    anyhow::ensure!(\n        envelope_type_name == custom_type_name.as_str(),\n        \"CustomData envelope type '{envelope_type_name}' does not match message type '{custom_type_name}'\"\n    );\n\n    let Data::Custom(custom) = data else {\n        anyhow::bail!(\"CustomData registry returned non-custom data\");\n    };\n\n    Ok(Some(custom))\n}\n","sourceCodeStart":567,"sourceCodeEnd":596,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/msgbus/external/mod.rs#L567-L596","documentation":"CustomData payloads are decoded by looking up a registered custom data type from the envelope's 'type' field, then verifying the envelope type name equals the registered message type name. If the JSON envelope's 'type' does not match the type the registry resolved for the payload, decoding aborts because the envelope and body would disagree about what data is being carried.","triggerScenarios":"Calling decode_custom_data_payload on JSON whose 'type' string differs from the custom data type name registered for the message; e.g. publishing {'type': 'MyIndicator', ...} while the payload registers/decodes as 'MyOtherType', or type names changed between versions.","commonSituations":"Renaming a custom data class after persisting messages, publishing with a hand-written 'type' string that has a typo or wrong casing, cross-version replay where the custom type was renamed, or registering the wrong custom type for a topic.","solutions":["Ensure the JSON 'type' field exactly equals the custom data class name (to_string of the registered type)","Re-serialize/re-stamp old persisted messages after renaming a custom data type","Check for case or namespace differences between the envelope 'type' and the registered type name","Verify the CustomData registration (registry mapping) points at the same type used to publish"],"exampleFix":"// before\nlet msg = json!({\"type\": \"Quotes\", \"data\": body});\n// after (custom type registered as \"QuoteTick\")\nlet msg = json!({\"type\": \"QuoteTick\", \"data\": body});","handlingStrategy":"validation","validationCode":"def assert_envelope_matches(msg: dict, registered_type: str) -> None:\n    t = msg.get(\"type\")\n    if not isinstance(t, str) or not t:\n        raise ValueError(\"CustomData JSON missing 'type' field\")\n    if t != registered_type:\n        raise ValueError(f\"envelope type '{t}' != registered '{registered_type}'\")","typeGuard":"def envelope_matches(msg: dict, expected: str) -> bool:\n    return isinstance(msg, dict) and msg.get(\"type\") == expected","tryCatchPattern":"try:\n    data = decode_custom_data_payload(json_bytes)\nexcept ValueError as e:\n    log.error(f\"CustomData decode failed: {e}\")\n    metrics.decode_mismatch.inc()\n    return None","preventionTips":["Derive the envelope 'type' programmatically from type(custom).__name__ instead of hand-writing it","Keep a registry test that round-trips every custom data type through serialize/decode","When renaming custom data classes, migrate persisted messages and bump a schema version","Watch for casing/namespace drift between publishers in different languages"],"tags":["serialization","custom-data","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}