nautechsystems/nautilus_trader · error

fee exponent must be non-negative

Error message

fee exponent must be non-negative

What it means

instrument_fee_exponent extracts the fee exponent from the venue fee schedule JSON and requires it to be a numeric value >= 0. The exponent drives the fee curve rate; a negative exponent would produce nonsensical fee scaling, so the parser bails with "fee exponent must be non-negative".

Source

Thrown at crates/adapters/polymarket/src/execution/parse.rs:480

pub fn instrument_fee_exponent(instrument: &InstrumentAny) -> anyhow::Result<Decimal> {
    let value = match instrument {
        InstrumentAny::BinaryOption(bo) => {
            bo.info.as_ref().and_then(|info| info.get("fee_schedule"))
        }
        _ => None,
    };
    let Some(schedule) = value else {
        return Ok(Decimal::ONE);
    };
    let value = schedule
        .get("exponent")
        .context("fee schedule is missing exponent")?;
    let exponent = match value {
        serde_json::Value::String(value) => parse_decimal_exact(value)?,
        serde_json::Value::Number(value) => parse_decimal_exact(&value.to_string())?,
        _ => anyhow::bail!("fee exponent must be a decimal number or numeric string"),
    };
    anyhow::ensure!(
        exponent >= Decimal::ZERO,
        "fee exponent must be non-negative"
    );
    Ok(exponent)
}

/// Adjusts a market-BUY pUSD amount to fit within the user's pUSD balance once
/// platform and builder taker fees are deducted. Mirrors `adjust_market_buy_amount`
/// in `polymarket-rs-clob-client-v2`'s `clob/utilities.rs`.
///
/// Returns `amount` unchanged when the balance already covers `amount + fees`.
/// Otherwise solves for the principal that, with fees, exactly consumes the
/// balance, then truncates to `USDC_DECIMALS` (the on-chain pUSD scale).
///
/// The fee-curve step `(p * (1 - p))^exponent` is the only computation that
/// crosses into `f64`, matching the reference SDK so we agree with the
/// venue's authoritative match-time fee calculation regardless of whether
/// Polymarket ships a fractional exponent in the future.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the fee schedule payload's exponent field and correct it to a non-negative value.
  2. Refresh the fee schedule from the venue (delete stale cached fee config and re-fetch).
  3. Confirm exponent conventions: if the schedule is meant to express 10^-n rates, store n as a non-negative integer.

Example fix

// before
{"exponent": "-2"}  // negative -> rejected
// after
{"exponent": "2"}   // non-negative fee-curve exponent
Defensive patterns

Strategy: validation

Validate before calling

let exponent: Decimal = parse_decimal_exact(schedule["exponent"].as_str().unwrap_or_default())?;
if exponent < Decimal::ZERO { return Err("fee schedule exponent must be >= 0".into()); }

Type guard

fn has_valid_fee_exponent(schedule: &serde_json::Value) -> bool {
    schedule.get("exponent")
        .and_then(|v| v.as_str().or_else(|| v.as_number().map(|n| n.to_string().into())).map(|s| s.parse::<Decimal>().map(|d| d >= Decimal::ZERO).unwrap_or(false)))
        .unwrap_or(false)
}

Try / catch

let exponent = match instrument_fee_exponent(&schedule) {
    Ok(e) => e,
    Err(e) => { tracing::warn!("bad fee schedule: {e:#}; refreshing from venue"); refresh_fee_schedule().await?; }
};

Prevention

When it happens

Trigger: Parsing a Polymarket fee schedule whose exponent field is a negative number (string or JSON number), via calculate_commission, build_admitted_target_fill, build_fill_reports_from_trades, build_ws_taker_fill_report, or submit_market_order.

Common situations: Stale or hand-edited cached fee-schedule JSON; a venue fee-model change emitting a different exponent convention; test fixtures with negative exponent values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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