nautechsystems/nautilus_trader · error
Executed amount must be positive
Error message
Executed amount must be positive
What it means
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.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:5251
match side {
OrderSide::Sell => Ok((base, quote)),
OrderSide::Buy => Ok((quote, base)),
}
}
fn fill_price_from_quote(
last_qty: Quantity,
quote_amount: U256,
quote_currency: Currency,
) -> anyhow::Result<Price> {
let quote = Money::from_u256(quote_amount, quote_currency)?;
Price::from_decimal_dp(quote.as_decimal() / last_qty.as_decimal(), FIXED_PRECISION)
.map_err(anyhow::Error::from)
}
fn raw_amount_to_quantity(amount: U256, decimals: u8) -> anyhow::Result<Quantity> {
if amount.is_zero() {
anyhow::bail!("Executed amount must be positive");
}
let quantity = if decimals >= FIXED_PRECISION {
let scale = U256::from(10u64)
.checked_pow(U256::from(decimals - FIXED_PRECISION))
.ok_or_else(|| anyhow::anyhow!("Executed amount scaling overflow"))?;
Quantity::from_u256(amount / scale, FIXED_PRECISION).map_err(anyhow::Error::from)?
} else {
Quantity::from_u256(amount, decimals).map_err(anyhow::Error::from)?
};
if quantity.is_zero() {
anyhow::bail!(
"Executed amount {amount} is below representable quantity precision {FIXED_PRECISION}"
);
}
Ok(quantity)
}
View on GitHub (pinned to 18893faf8b)
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
Example fix
// before
let qty = raw_amount_to_quantity(U256::zero(), decimals)?;
// after
if amount.is_zero() { tracing::warn!("skipping zero-amount execution"); return Ok(None); }
let qty = raw_amount_to_quantity(amount, decimals)?; Defensive patterns
Strategy: validation
Validate before calling
if amount.is_zero() {
return Err(anyhow::anyhow!("refusing to convert zero executed amount"));
}
let qty = raw_amount_to_quantity(amount, decimals)?; Type guard
fn is_positive_amount(a: alloy_primitives::U256) -> bool { !a.is_zero() } Try / catch
match raw_amount_to_quantity(amount, decimals) {
Ok(q) => handle(q),
Err(e) if e.to_string().contains("must be positive") => skip_zero_fill(),
Err(e) => return Err(e),
} Prevention
- Filter zero-amount events at the decoding layer before conversion
- Unit-test the converter with zero, dust, and max U256 inputs
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid venue {}, expected Blockchain DEX format
- `router_addresses` must contain at least one router address
- Quote spend limit for {token_in} -> {token_out} is denominat
- Pool identifier {pool_identifier} is a pool ID; only address
- {context} verification is locally invalid
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7dd7ad515e1d93a5.
Report an issue: GitHub.