{"record":{"id":"7e696da827a975c4","repo":"nautechsystems/nautilus_trader","slug":"failed-to-decode-position-replay-state-e-7e696d","errorCode":null,"errorMessage":"Failed to decode position replay state: {e}","messagePattern":"Failed to decode position replay state: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/sql/queries.rs","lineNumber":942,"sourceCode":"        .map(|rows| rows.into_iter().map(|row| row.0).collect())\n        .map_err(|e| anyhow::anyhow!(\"Failed to load position events: {e}\"))\n    }\n\n    /// Loads and replays a complete `Position` for a `position_id` via the provided `pool`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if loading events, loading instruments, or replaying fills fails.\n    pub async fn load_position(\n        pool: &PgPool,\n        position_id: &PositionId,\n    ) -> anyhow::Result<Option<Position>> {\n        if let Some(snapshot) = Self::load_position_snapshot(pool, position_id).await?\n            && let Some(replay_state) = snapshot.replay_state\n        {\n            return serde_json::from_value(replay_state)\n                .map(Some)\n                .map_err(|e| anyhow::anyhow!(\"Failed to decode position replay state: {e}\"));\n        }\n\n        let fills = Self::load_position_events(pool, position_id).await?;\n        let Some((first_fill, remaining_fills)) = fills.split_first() else {\n            return Ok(None);\n        };\n        let Some(instrument) = Self::load_instrument(pool, &first_fill.instrument_id).await? else {\n            log::error!(\n                \"Instrument not found for position {position_id}: {}\",\n                first_fill.instrument_id\n            );\n            return Ok(None);\n        };\n\n        let mut position = Position::new(&instrument, first_fill.clone());\n        for fill in remaining_fills {\n            if position.trade_ids().contains(&fill.trade_id) {\n                anyhow::bail!(","sourceCodeStart":924,"sourceCodeEnd":960,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/sql/queries.rs#L924-L960","documentation":"load_position found a position snapshot whose replay_state JSON value failed serde_json deserialization into the expected replay state type. This error means the persisted snapshot is present but its embedded replay state does not match the expected structure.","triggerScenarios":"Calling load_position(pool, position_id) where load_position_snapshot returns a snapshot with a replay_state that serde_json cannot deserialize — wrong shape, missing fields, or type changes between versions.","commonSituations":"Snapshot written by an older nautilus version whose Position replay state schema changed; manually edited snapshot rows; truncated/corrupted JSON stored in the snapshot column.","solutions":["Inspect the wrapped serde_json error to see which field/type failed.","Compare the stored replay_state JSON against the current Rust struct and migrate old snapshots.","If snapshots are optional for you, clear the snapshot row so load_position falls back to replaying raw fill events via load_position_events.","Ensure writes and reads use the same nautilus version, or run the provided data migrations."],"exampleFix":"// before\nlet position = DatabaseQuery::load_position(&pool, &position_id).await?;\n// after\nlet position = match DatabaseQuery::load_position(&pool, &position_id).await {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"Failed to decode position replay state\") => {\n        tracing::warn!(\"Snapshot unusable for {position_id}, rebuilding from events: {e:#}\");\n        None // fall back to event replay / re-snapshot\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match load_position(&pool, &pid).await {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"Failed to decode position replay state\") => {\n        rebuild_from_events(&pool, &pid).await.ok()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use the same nautilus version for snapshot writes and reads.","Run schema/data migrations on version upgrades.","Prefer deleting an invalid snapshot row over editing it — event replay rebuilds state."],"tags":["rust","serde","json","deserialization","snapshot"],"backgroundTag":"json-unmarshal-failed","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"}