nautechsystems/nautilus_trader · error

Derived minimum output is zero for quoted output {quoted_amo

Error message

Derived minimum output is zero for quoted output {quoted_amount_out} at {slippage_bps} bps slippage

What it means

After applying slippage, if the derived minimum output rounds down to zero the swap would have no slippage protection (any output accepted). The adapter bails with the quoted output and slippage values to force the caller to use a tighter slippage or larger trade size.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:5295

    if !amount.is_negative() {
        anyhow::bail!("Swap quote output amount {amount} is not a positive output");
    }
    Ok(amount.unsigned_abs())
}

/// Derives the minimum output accepted for the swap: the quoted output reduced by
/// `slippage_bps`, in exact integer arithmetic. Rejects a zero minimum, which would leave
/// the swap without slippage protection.
fn derive_min_amount_out(quoted_amount_out: U256, slippage_bps: u32) -> anyhow::Result<U256> {
    if slippage_bps >= BPS_DENOMINATOR {
        anyhow::bail!("Slippage {slippage_bps} bps must be below {BPS_DENOMINATOR}");
    }
    let min_amount_out = quoted_amount_out
        .checked_mul(U256::from(BPS_DENOMINATOR - slippage_bps))
        .and_then(|scaled| scaled.checked_div(U256::from(BPS_DENOMINATOR)))
        .ok_or_else(|| anyhow::anyhow!("Minimum output derivation overflow"))?;
    if min_amount_out.is_zero() {
        anyhow::bail!(
            "Derived minimum output is zero for quoted output {quoted_amount_out} at {slippage_bps} bps slippage"
        );
    }
    Ok(min_amount_out)
}

impl BlockchainExecutionClient {
    async fn build_execution_verification_migration(
        &self,
        snapshot: ExecutionVerificationMigrationSnapshot,
        finalized: VerifiedBlockHeader,
        finalized_headers: &[VerifiedBlockHeader],
        nonce_verification: &Verified<u64>,
    ) -> anyhow::Result<ExecutionVerificationMigration> {
        let next_canonical_nonce = nonce_verification.value;
        let mut hashes_by_intent: HashMap<i64, Vec<&ExecutionTransactionHashRow>> = HashMap::new();
        for hash in &snapshot.hashes {
            hashes_by_intent

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Reduce slippage_bps so the derived minimum stays positive
  2. Increase the trade size; the quoted output is too small to protect
  3. Treat as dust and skip the trade; failing open with min 0 is unsafe

Example fix

// before
let min_out = derive_min_amount_out(U256::from(3u8), 5000)?; // -> 0
// after
let min_out = derive_min_amount_out(U256::from(3_000_000u32), 100)?; // keep min > 0
Defensive patterns

Strategy: validation

Validate before calling

let bps = slippage_bps.min(9_999);
if quoted_amount_out * U256::from(BPS_DENOMINATOR - bps) < U256::from(BPS_DENOMINATOR) {
    return Err(anyhow::anyhow!("quote too small for slippage; min would round to 0"));
}

Try / catch

match derive_min_amount_out(quoted, bps) {
    Ok(min) => swap(min),
    Err(e) if e.to_string().contains("is zero") => skip_dust_trade(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: derive_min_amount_out with a very small quoted_amount_out relative to slippage_bps, e.g. quoted output of a few wei with 1000+ bps slippage, so quoted * (10000-bps) / 10000 truncates to 0.

Common situations: Tiny dust-sized swaps; extremely wide slippage tolerance on a small quote; illiquid pool returning near-zero quotes.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/7b373cb90955dfbc. Report an issue: GitHub.