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 theView on GitHub (pinned to 18893faf8b)
Solutions
- Log/dump the raw fee schedule JSON to inspect the actual type of "exponent"
- Fix the fixture/mocked response so exponent is a number or numeric string
- Upgrade the adapter if the Polymarket API changed the fee schedule schema
- Add a validation step on the API response before feeding it to fee parsing
- 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
- Validate fee schedule JSON shape (exponent is number/string) before parsing
- Keep test fixtures in sync with the live API schema
- Log raw payloads when fee parsing fails to catch upstream schema changes
- Pin and monitor the Polymarket API version your fixtures target
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
- invalid RTDS JSON frame
- Expected 2 token IDs, received {}
- Expected 2 outcomes, received {}
- Unsupported RTDS custom data type: {other}
- Failed to parse clob_token_ids '{}': {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ed566ca44cb29e4f.
Report an issue: GitHub.