{"record":{"id":"b2f5de50dd2c544e","repo":"nautechsystems/nautilus_trader","slug":"customdata-json-missing-payload-field","errorCode":null,"errorMessage":"CustomData JSON missing 'payload' field","messagePattern":"CustomData JSON missing 'payload' field","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/registry.rs","lineNumber":127,"sourceCode":"            let p: Params = serde_json::from_value(m.clone()).ok()?;\n            if p.is_empty() { None } else { Some(p) }\n        }\n    });\n    let identifier = obj\n        .get(\"identifier\")\n        .and_then(|v| v.as_str())\n        .map(String::from);\n    Some(DataType::new(type_name, metadata, identifier))\n}\n\n/// Parses the canonical `CustomData` JSON envelope `{ type, data_type, payload }` and returns\n/// the payload value to pass to the registered type deserializer. Does not depend on\n/// user payload field names.\nfn parse_envelope_payload(value: &serde_json::Value) -> Result<serde_json::Value, anyhow::Error> {\n    let payload = value\n        .get(\"payload\")\n        .cloned()\n        .ok_or_else(|| anyhow::anyhow!(\"CustomData JSON missing 'payload' field\"))?;\n    Ok(payload)\n}\n\n/// Looks up and runs the JSON deserializer for the given type name.\n/// Returns `None` if the type is not registered.\n///\n/// # Errors\n/// Returns an error if the deserializer fails.\npub fn deserialize_custom_from_json(\n    type_name: &str,\n    value: &serde_json::Value,\n) -> Result<Option<Data>, anyhow::Error> {\n    let reg = registries();\n    let deserializer_ref = match reg.json.get(type_name) {\n        Some(d) => d,\n        None => return Ok(None),\n    };\n    let data_type = parse_data_type_from_value(value);","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/registry.rs#L109-L145","documentation":"CustomData is serialized with an envelope that separates routing/metadata fields from the user payload under a reserved `\"payload\"` key. `parse_envelope_payload` extracts that key before handing it to the registered type deserializer; if it's missing, the envelope is malformed and this error is raised.","triggerScenarios":"Calling `deserialize_custom_from_json` (which calls `parse_envelope_payload`) with JSON that was serialized without the envelope — e.g. raw user-type JSON, hand-written JSON, or output from a different/older serializer version.","commonSituations":"Version skew between the writer (old format without envelope) and reader; manually constructing CustomData JSON in tests or configs and forgetting the `payload` wrapper; a serialization bug on the producing side dropping the field.","solutions":["Serialize with `CustomData`'s own serializer so the `payload` envelope field is written; never hand-build the JSON.","Check producer/consumer library versions match; re-serialize old records with the current format.","Inspect the JSON at hand: wrap the user data as `{ ..., \"payload\": <user data> }` if you must construct it manually.","Before deserializing, guard with `value.get(\"payload\").is_some()` and log the raw JSON when absent."],"exampleFix":"// before\nlet data = deserialize_custom_from_json(r#\"{\"price\": 1.5}\"#)?;\n\n// after\nlet data = deserialize_custom_from_json(r#\"{\"type_name\": \"MyQuote\", \"payload\": {\"price\": 1.5}}\"#)?;","handlingStrategy":"validation","validationCode":"fn has_payload(v: &serde_json::Value) -> bool {\n    v.get(\"payload\").is_some()\n}","typeGuard":"fn payload_of(v: &serde_json::Value) -> Option<serde_json::Value> {\n    v.get(\"payload\").cloned()\n}","tryCatchPattern":"match deserialize_custom_from_json(s) {\n    Ok(d) => d,\n    Err(e) if e.to_string().contains(\"payload\") => {\n        log::error!(\"malformed CustomData envelope (missing payload): {e}\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always serialize CustomData through its own serializer so the payload envelope is present.","Keep producer and consumer library versions in sync regarding the envelope format.","When hand-crafting CustomData JSON (tests/configs), always wrap user data under \"payload\"."],"tags":["rust","json","deserialization","custom-data"],"backgroundTag":"missing-required-argument","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"}