{"record":{"id":"9af263f49f5aa602","repo":"nautechsystems/nautilus_trader","slug":"openpositions-failed-to-build-quantity-for-inst","errorCode":null,"errorMessage":"OpenPositions: failed to build Quantity for {inst_id}: {e:?}","messagePattern":"OpenPositions: failed to build Quantity for (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/kraken/src/http/spot/client.rs","lineNumber":2446,"sourceCode":"        }\n\n        let mut reports = Vec::new();\n\n        for (_, (signed_qty, inst_id)) in agg {\n            let instrument = self\n                .get_cached_instrument(&inst_id.symbol.inner())\n                .ok_or_else(|| InstrumentLookupError::not_found(inst_id))?;\n\n            let side = if signed_qty.is_sign_positive() && !signed_qty.is_zero() {\n                PositionSide::Long\n            } else if signed_qty.is_sign_negative() && !signed_qty.is_zero() {\n                PositionSide::Short\n            } else {\n                PositionSide::Flat\n            };\n            let quantity = Quantity::from_decimal_dp(signed_qty.abs(), instrument.size_precision())\n                .map_err(|e| {\n                    anyhow::anyhow!(\"OpenPositions: failed to build Quantity for {inst_id}: {e:?}\")\n                })?;\n            let report = PositionStatusReport::new(\n                account_id, inst_id, side, quantity, ts_init, ts_init, None, None, None,\n            );\n            reports.push(report);\n        }\n\n        // If a specific instrument was requested but no open position exists for it, emit\n        // a FLAT report so the engine can reconcile a previously-open position to closed.\n        // (Kraken omits fully-closed positions from OpenPositions entirely.)\n        // Only emit for instruments known to this spot client; a missing cache entry means\n        // the target belongs to a different product type (e.g. futures) and must not receive\n        // a spurious FLAT from the spot reconciliation path.\n        if let Some(target_id) = instrument_id {\n            let already_reported = reports.iter().any(|r| r.instrument_id == target_id);\n\n            if !already_reported\n                && let Some(instrument) = self.get_cached_instrument(&target_id.symbol.inner())","sourceCodeStart":2428,"sourceCodeEnd":2464,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/kraken/src/http/spot/client.rs#L2428-L2464","documentation":"After resolving the instrument, the adapter converts the position volume into a Nautilus `Quantity` using the instrument's `size_precision`. `Quantity::from_decimal_dp` fails when the parsed value cannot be represented at that precision, and the adapter wraps the debug error in this anyhow message. It means Kraken-reported volume is incompatible with the instrument's size precision.","triggerScenarios":"OpenPositions parsing where `pos.vol` (parsed via `Decimal::from_str_exact`) has more decimal places than `instrument.size_precision()`, is negative in an unexpected way, or otherwise cannot be rounded/represented as a Quantity at the instrument precision.","commonSituations":"Stale or wrong instrument definition whose size_precision does not match the venue's current precision; Kraken changes pair volume precision; corrupted/unexpected `vol` string in the API response (e.g. scientific notation the exact parser rejects).","solutions":["Update the instrument definition so `size_precision` matches Kraken's `lot_decimals` for the pair.","Inspect the inner `{e:?}` for the exact precision/parse problem and the offending `vol` value.","Re-fetch instruments to refresh precision after Kraken announces pair changes.","If the value is genuinely over-precise, round to the instrument precision at the strategy layer before it reaches the venue volume, or report the venue data as unexpected."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn validate_volume(vol: &str, size_precision: u8) -> Result<(), String> {\n    let d = rust_decimal::Decimal::from_str_exact(vol)\n        .map_err(|e| format!(\"bad vol {vol}: {e}?\"))?;\n    let scale = d.scale();\n    if scale as u8 > size_precision {\n        Err(format!(\"vol {vol} has {} dp, instrument allows {}\n        \", scale, size_precision))\n    } else { Ok(()) }\n}","typeGuard":null,"tryCatchPattern":"match client.request_open_positions().await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"failed to build Quantity\") => {\n        log::warn!(\"venue volume incompatible with instrument precision: {e}\");\n        Vec::new() // skip bad position, alert operator\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep instrument size_precision synced with Kraken lot_decimals","Refresh instrument definitions when Kraken changes pair specs","Reconcile positions incrementally so a single bad value doesn't block the whole report"],"tags":["quantity","precision","kraken","positions","decimal"],"backgroundTag":"value-out-of-range","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"}