{"record":{"id":"7dd7ad515e1d93a5","repo":"nautechsystems/nautilus_trader","slug":"executed-amount-must-be-positive","errorCode":null,"errorMessage":"Executed amount must be positive","messagePattern":"Executed amount must be positive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":5251,"sourceCode":"    match side {\n        OrderSide::Sell => Ok((base, quote)),\n        OrderSide::Buy => Ok((quote, base)),\n    }\n}\n\nfn fill_price_from_quote(\n    last_qty: Quantity,\n    quote_amount: U256,\n    quote_currency: Currency,\n) -> anyhow::Result<Price> {\n    let quote = Money::from_u256(quote_amount, quote_currency)?;\n    Price::from_decimal_dp(quote.as_decimal() / last_qty.as_decimal(), FIXED_PRECISION)\n        .map_err(anyhow::Error::from)\n}\n\nfn raw_amount_to_quantity(amount: U256, decimals: u8) -> anyhow::Result<Quantity> {\n    if amount.is_zero() {\n        anyhow::bail!(\"Executed amount must be positive\");\n    }\n    let quantity = if decimals >= FIXED_PRECISION {\n        let scale = U256::from(10u64)\n            .checked_pow(U256::from(decimals - FIXED_PRECISION))\n            .ok_or_else(|| anyhow::anyhow!(\"Executed amount scaling overflow\"))?;\n        Quantity::from_u256(amount / scale, FIXED_PRECISION).map_err(anyhow::Error::from)?\n    } else {\n        Quantity::from_u256(amount, decimals).map_err(anyhow::Error::from)?\n    };\n\n    if quantity.is_zero() {\n        anyhow::bail!(\n            \"Executed amount {amount} is below representable quantity precision {FIXED_PRECISION}\"\n        );\n    }\n    Ok(quantity)\n}\n","sourceCodeStart":5233,"sourceCodeEnd":5269,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L5233-L5269","documentation":"raw_amount_to_quantity converts a raw on-chain U256 executed amount into a domain Quantity. The adapter rejects a zero raw amount because an executed fill/swap of zero is meaningless and cannot be represented as a positive quantity.","triggerScenarios":"Calling raw_amount_to_quantity with amount = U256::zero(), typically when an on-chain event (fill or swap receipt) reports an executed amount of 0.","commonSituations":"Indexing a swap/fill event whose amount fields decoded to zero (e.g. dust, transfer event with 0 value, or misparsed log topics); replaying stale or synthetic events.","solutions":["Check the decoded executed amount is > 0 before converting; skip or drop zero-amount events","Verify event parsing maps the correct log field (amount0/amount1) so real amounts aren't lost","Log and skip the offending event instead of failing the whole indexing loop"],"exampleFix":"// before\nlet qty = raw_amount_to_quantity(U256::zero(), decimals)?;\n// after\nif amount.is_zero() { tracing::warn!(\"skipping zero-amount execution\"); return Ok(None); }\nlet qty = raw_amount_to_quantity(amount, decimals)?;","handlingStrategy":"validation","validationCode":"if amount.is_zero() {\n    return Err(anyhow::anyhow!(\"refusing to convert zero executed amount\"));\n}\nlet qty = raw_amount_to_quantity(amount, decimals)?;","typeGuard":"fn is_positive_amount(a: alloy_primitives::U256) -> bool { !a.is_zero() }","tryCatchPattern":"match raw_amount_to_quantity(amount, decimals) {\n    Ok(q) => handle(q),\n    Err(e) if e.to_string().contains(\"must be positive\") => skip_zero_fill(),\n    Err(e) => return Err(e),\n}","preventionTips":["Filter zero-amount events at the decoding layer before conversion","Unit-test the converter with zero, dust, and max U256 inputs"],"tags":["blockchain","quantity-conversion","validation"],"backgroundTag":"invalid-argument-value","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"}