nautechsystems/nautilus_trader · error

{field} {value} must be non-negative

Error message

{field} {value} must be non-negative

What it means

validate_quantity_evidence enforces that a quantity-like decimal from provider evidence is usable as a domain Quantity. When allow_zero is set, the value must be >= 0; this error fires for a negative value where zero is permitted (e.g. a cumulative/cancelled quantity).

Source

Thrown at crates/adapters/polymarket/src/execution/reconciliation.rs:274

    let instrument_outcome = binary
        .outcome
        .context("Polymarket instrument is missing outcome metadata")?;
    anyhow::ensure!(
        instrument_outcome == outcome.as_str(),
        "provider outcome {outcome} does not match instrument outcome {instrument_outcome}",
    );

    Ok(())
}

fn validate_quantity_evidence(
    value: Decimal,
    precision: u8,
    field: &str,
    allow_zero: bool,
) -> anyhow::Result<()> {
    if allow_zero {
        anyhow::ensure!(
            value >= Decimal::ZERO,
            "{field} {value} must be non-negative"
        );
    } else {
        anyhow::ensure!(value > Decimal::ZERO, "{field} {value} must be positive");
    }

    let quantity = Quantity::from_decimal_dp(value, precision).with_context(|| {
        format!("failed to represent {field} {value} with quantity precision {precision}")
    })?;
    anyhow::ensure!(
        quantity.as_decimal() == value,
        "{field} {value} is not exactly representable with quantity precision {precision}",
    );
    Ok(())
}

fn validate_price_evidence(value: Decimal, precision: u8, field: &str) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw provider field and check whether the endpoint returns signed deltas; convert to absolute values before validation.
  2. Verify upstream arithmetic is not accidentally negating the value (e.g. size_remaining = size - filled with bad inputs).
  3. Skip or clamp obviously invalid negative rows only after confirming they represent no-op events.
  4. Report/provider-side: check for API version changes that introduced signed semantics.

Example fix

// before
validate_quantity_evidence(row.size, precision, "size", true)?;
// after
let size = row.size;
anyhow::ensure!(size >= Decimal::ZERO, "provider size must be non-negative, got {size}");
validate_quantity_evidence(size, precision, "size", true)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_non_negative(v: Decimal) -> bool { v >= Decimal::ZERO }

Try / catch

match validate_quantity_evidence(value, precision, field, true) {
    Ok(()) => (),
    Err(e) if e.to_string().contains("non-negative") => warn!("skipping negative {} for {}", field, trade_id),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: validate_order_row_values or validate_trade_values passes a negative Decimal for a field validated with allow_zero=true (e.g. negative size, negative filled quantity from the provider).

Common situations: Provider API returning signed amounts for cancellations or decreases; subtracting values upstream and passing a delta instead of an absolute quantity; sign-convention changes in provider payloads.

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/c47b2ae322e8162b. Report an issue: GitHub.