nautechsystems/nautilus_trader · error · anyhow::Error

No quote spend ceiling for BUY token pair {} -> {}

Error message

No quote spend ceiling for BUY token pair {} -> {}

What it means

In `verified_swap_amounts` (crates/adapters/blockchain/src/execution/client.rs:4094), BUY orders route tokens with the swap's input amount coming from the quote, so a hard ceiling on how much quote token may be spent is mandatory. This error means the SwapPlan for a BUY order has `quote_spend_ceiling == None`, so the client cannot bound the spend and aborts instead of executing an unbounded trade.

Source

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

    }
}

fn verified_swap_amounts(plan: &SwapPlan, quote: UniswapV3Quote) -> anyhow::Result<(U256, U256)> {
    anyhow::ensure!(
        !quote.amount.is_zero(),
        "Independent swap quote returned zero"
    );
    let base_amount =
        quantity_to_raw_amount(plan.order.quantity(), plan.pool.get_base_token().decimals)?;
    let slippage_bps = plan.slippage_bps;
    match plan.order.order_side() {
        OrderSide::Sell => Ok((
            base_amount,
            derive_min_amount_out(quote.amount, slippage_bps)?,
        )),
        OrderSide::Buy => {
            let ceiling = plan.quote_spend_ceiling.ok_or_else(|| {
                anyhow::anyhow!(
                    "No quote spend ceiling for BUY token pair {} -> {}",
                    plan.token_in,
                    plan.token_out
                )
            })?;
            anyhow::ensure!(
                quote.amount <= ceiling.max_amount,
                "BUY quote amount {} exceeds the configured quote-spend maximum {} for {} -> {}",
                quote.amount,
                ceiling.max_amount,
                plan.token_in,
                plan.token_out
            );
            Ok((
                quote.amount,
                derive_min_amount_out(base_amount, slippage_bps)?,
            ))
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set quote_spend_ceiling (with max_amount in quote-token raw units) on the SwapPlan for every BUY order.
  2. If building plans from config, ensure the quote-spend maximum is loaded and validated for buy-side orders before plan creation.
  3. Only use OrderSide::Buy when the caller can supply a spend ceiling; otherwise convert the intent to a Sell-side formulation.
  4. Reject BUY plans without a ceiling at plan construction with a clear error instead of at quote time.

Example fix

// before
let plan = SwapPlan {
    order,
    token_in,
    token_out,
    quote_spend_ceiling: None,
    ..plan_base
};
// after
let plan = SwapPlan {
    order,
    token_in,
    token_out,
    quote_spend_ceiling: Some(QuoteSpendCeiling { max_amount: max_quote_spend_raw }),
    ..plan_base
};
Defensive patterns

Strategy: validation

Validate before calling

if plan.order.order_side() == OrderSide::Buy && plan.quote_spend_ceiling.is_none() {
    return Err("BUY plan requires quote_spend_ceiling");
}

Type guard

fn has_buy_ceiling(plan: &SwapPlan) -> bool {
    plan.order.order_side() != OrderSide::Buy || plan.quote_spend_ceiling.is_some()
}

Try / catch

match client.execute_swap(plan).await {
    Err(e) if e.to_string().contains("No quote spend ceiling") => {
        // rebuild the plan with a ceiling and retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Constructing a SwapPlan with order_side()==Buy but leaving quote_spend_ceiling unset, then running it through quote verification / verified_swap_amounts.

Common situations: An adapter or strategy builder that sets the ceiling only for SELL plans; a config loader that omits the max-spend field; refactored code paths that construct SwapPlan manually without the ceiling; an upgrade where quote_spend_ceiling was newly introduced and old callers were not migrated.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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