nautechsystems/nautilus_trader · error

fee exponent must be a decimal number or numeric string

Error message

fee exponent must be a decimal number or numeric string

What it means

instrument_fee_exponent extracts the fee schedule's "exponent" field from the Polymarket API response and requires it to be a JSON string or number that parses to an exact Decimal. Any other JSON type (object, array, bool, null) fails with this error, since the fee exponent cannot be interpreted.

Source

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

///
/// Returns an error if a present schedule has a missing, invalid, or negative exponent.
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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log/dump the raw fee schedule JSON to inspect the actual type of "exponent"
  2. Fix the fixture/mocked response so exponent is a number or numeric string
  3. Upgrade the adapter if the Polymarket API changed the fee schedule schema
  4. Add a validation step on the API response before feeding it to fee parsing
  5. Contact Polymarket support / check API docs if production responses are malformed

Example fix

// before (fixture)
{"maker": {"exponent": {"value": 2}}}
// after
{"maker": {"exponent": 2}}
Defensive patterns

Strategy: type-guard

Validate before calling

fn exponent_is_valid(schedule: &serde_json::Value) -> bool {
    matches!(
        schedule.get("exponent"),
        Some(serde_json::Value::String(_) | serde_json::Value::Number(_))
    )
}

Type guard

fn as_exponent(value: &serde_json::Value) -> Option<&serde_json::Value> {
    match value.get("exponent")? {
        v @ (serde_json::Value::String(_) | serde_json::Value::Number(_)) => Some(v),
        _ => None,
    }
}

Try / catch

match build_fill_reports_from_trades(&trades, &fee_schedule) {
    Err(e) if e.to_string().contains("fee exponent must be a decimal") => {
        error!("malformed fee schedule JSON: {e:#}; dump raw payload for diagnosis");
    }
    Err(e) => return Err(e),
    Ok(reports) => {},
}

Prevention

When it happens

Trigger: Parsing a fee schedule (from fee rate queries or trade/fill payloads) whose "exponent" key holds a non-numeric, non-string JSON value such as null, a bool, an object, or an array.

Common situations: Polymarket API schema change emitting exponent as an object or null; mocked/test fixtures with wrong JSON shape; proxy or cached responses returning error objects in place of the schedule; hand-written fixtures with malformed exponent.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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