nautechsystems/nautilus_trader · error

Invalid negative quantity: {value}

Error message

Invalid negative quantity: {value}

What it means

`decode_optional_quantity` converts an i64 Databento quantity into an `Option<Quantity>`: `i64::MAX` maps to `None` (undefined), non-negative values decode normally, and negative values are rejected because quantities in the Nautilus domain model can never be negative. The error surfaces when a Databento record carries a negative quantity that would corrupt downstream order/position logic.

Source

Thrown at crates/adapters/databento/src/decode/primitives.rs:328

/// Decodes a quantity from the given value, expressed in standard whole-number units.
#[inline(always)]
#[must_use]
pub fn decode_quantity(value: u64) -> Quantity {
    quantity_from_whole(value)
}

/// Decodes a quantity from the given optional value, where `i64::MAX` indicates missing data.
///
/// # Errors
///
/// Returns an error if the quantity is negative.
#[inline(always)]
pub fn decode_optional_quantity(value: i64) -> anyhow::Result<Option<Quantity>> {
    match value {
        i64::MAX => Ok(None),
        value if value >= 0 => Ok(Some(quantity_from_whole(value as u64))),
        value => anyhow::bail!("Invalid negative quantity: {value}"),
    }
}

/// Decodes a timestamp, returning an error if undefined.
///
/// Databento uses `u64::MAX` as `UNDEF_TIMESTAMP` sentinel for null timestamps.
///
/// # Errors
///
/// Returns an error if `value` is `u64::MAX` (undefined).
#[inline(always)]
pub fn decode_timestamp(value: u64, field_name: &str) -> anyhow::Result<UnixNanos> {
    if value == dbn::UNDEF_TIMESTAMP {
        anyhow::bail!("Missing required timestamp for `{field_name}`")
    } else {
        Ok(UnixNanos::from(value))
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the offending record and verify whether the negative value is a genuine data issue; exclude or correct the record.
  2. If the semantics of the field allow negatives (e.g. net change), decode it with a signed type instead of `decode_optional_quantity`.
  3. Pre-filter records: skip rows where the quantity field is negative before calling the decoder.
  4. Catch the anyhow error in the record-processing closure and log/skip rather than aborting the whole range request.

Example fix

// before
let qty = decode_optional_quantity(stat.quantity as i64)?;
// after
let qty = if (stat.quantity as i64) < 0 && stat.quantity as i64 != i64::MAX { None } else { decode_optional_quantity(stat.quantity as i64)? };
Defensive patterns

Strategy: validation

Validate before calling

fn quantity_valid(raw: i64) -> bool { raw == i64::MAX || raw >= 0 }

Type guard

fn is_negative_quantity(v: i64) -> bool { v != i64::MAX && v < 0 }

Try / catch

match decode_optional_quantity(raw) {
    Ok(q) => use(q),
    Err(e) => { log::warn!("bad quantity: {e}"); Quantity::default() }
}

Prevention

When it happens

Trigger: `decode_optional_quantity(v)` called with `v < 0` (and not `i64::MAX`); typically via `decode_statistics_msg` when a statistics record contains a negative value quantity.

Common situations: Loading historical Databento statistics/definition data containing negative volume fields (data-quality issues or vendor encoding quirks), or feeding already-converted/scaled data back through the decoder so values appear negative.

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


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