{"record":{"id":"9185be1c28e6cadd","repo":"nautechsystems/nautilus_trader","slug":"serialized-position-did-not-contain-its-identifier","errorCode":null,"errorMessage":"serialized position did not contain its identifier","messagePattern":"serialized position did not contain its identifier","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":562,"sourceCode":"            }\n            let mut encoded = serde_json::to_value(event)?;\n            canonicalize_value(&mut encoded)?;\n            fills.push(json!({\n                \"client_order_id\": order.client_order_id().to_string(),\n                \"event\": encoded,\n                \"order_event_ordinal\": ordinal.to_string(),\n            }));\n        }\n    }\n    Ok(fills)\n}\n\nfn canonical_position(position: &Position) -> anyhow::Result<Value> {\n    let mut value = serde_json::to_value(position)?;\n    let object = value\n        .as_object_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"serialized position was not an object\"))?;\n    anyhow::ensure!(\n        object.remove(\"id\").is_some(),\n        \"serialized position did not contain its identifier\"\n    );\n    object.insert(\n        \"position_id\".to_string(),\n        Value::String(position.id.to_string()),\n    );\n    set_f64(object, \"avg_px_close\", position.avg_px_close);\n    set_f64(object, \"avg_px_open\", Some(position.avg_px_open));\n    set_f64(object, \"realized_return\", Some(position.realized_return));\n    set_f64(object, \"signed_qty\", Some(position.signed_qty));\n\n    if let Some(adjustments) = object.get_mut(\"adjustments\").and_then(Value::as_array_mut) {\n        for (source, encoded) in position.adjustments.iter().zip(adjustments) {\n            patch_position_adjustment(source, encoded)?;\n        }\n    }\n","sourceCodeStart":544,"sourceCodeEnd":580,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L544-L580","documentation":"Raised by `canonical_position` in the backtest results canonicalizer. After serializing a `Position` to JSON, the function removes the numeric `id` field and replaces it with `position_id` (the string form); if the serialized object lacks `id`, the invariant that every position carries an identifier is broken and the canonicalization fails via `anyhow::ensure!`. This guards the round-trip contract that downstream result consumers rely on for stable position identification.","triggerScenarios":"Calling the backtest result canonicalization path (e.g. when producing JSON output from `Position`) with a `Position` whose serde serialization omits the `id` field — typically `#[serde(skip)]`/`skip_serializing_if` on `id`, a custom `Serialize` impl that drops it, or a field renamed away from `id`.","commonSituations":"Upgrading the domain `Position` type after a nautilus_core refactor renames or moves the `id` field; hand-rolled test doubles or shim types implementing `Serialize` without `id`; feature-flagged serialization changes in the position model.","solutions":["Ensure the `Position` struct's `id` field is serialized as `\"id\"` (remove any `skip_serializing` or rename attributes).","Run `cargo test` for the backtest result serialization tests to confirm the serde contract.","If the field was renamed intentionally, update `canonical_position` to look up the new key and regenerate expected fixtures.","Check for stale generated bindings (cython/Rust codegen) mismatching the current `Position` schema and rebuild."],"exampleFix":"// before: #[serde(skip_serializing)] pub id: PositionId\n// after:\n#[serde(rename = \"id\")]\npub id: PositionId","handlingStrategy":"validation","validationCode":"let value = serde_json::to_value(&position)?;\nassert!(value.get(\"id\").is_some(), \"Position must serialize its id\");","typeGuard":"fn has_position_id(v: &serde_json::Value) -> bool {\n    v.as_object().map(|o| o.contains_key(\"id\")).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Never add skip_serializing to identifier fields on domain types.","Add a serialization round-trip test asserting `id` is present in JSON output."],"tags":["serialization","backtest","internal-invariant"],"backgroundTag":"internal-invariant-violation","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"}