{"record":{"id":"ed566ca44cb29e4f","repo":"nautechsystems/nautilus_trader","slug":"fee-exponent-must-be-a-decimal-number-or-numeric-s","errorCode":null,"errorMessage":"fee exponent must be a decimal number or numeric string","messagePattern":"fee exponent must be a decimal number or numeric string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/execution/parse.rs","lineNumber":478,"sourceCode":"///\n/// Returns an error if a present schedule has a missing, invalid, or negative exponent.\npub fn instrument_fee_exponent(instrument: &InstrumentAny) -> anyhow::Result<Decimal> {\n    let value = match instrument {\n        InstrumentAny::BinaryOption(bo) => {\n            bo.info.as_ref().and_then(|info| info.get(\"fee_schedule\"))\n        }\n        _ => None,\n    };\n    let Some(schedule) = value else {\n        return Ok(Decimal::ONE);\n    };\n    let value = schedule\n        .get(\"exponent\")\n        .context(\"fee schedule is missing exponent\")?;\n    let exponent = match value {\n        serde_json::Value::String(value) => parse_decimal_exact(value)?,\n        serde_json::Value::Number(value) => parse_decimal_exact(&value.to_string())?,\n        _ => anyhow::bail!(\"fee exponent must be a decimal number or numeric string\"),\n    };\n    anyhow::ensure!(\n        exponent >= Decimal::ZERO,\n        \"fee exponent must be non-negative\"\n    );\n    Ok(exponent)\n}\n\n/// Adjusts a market-BUY pUSD amount to fit within the user's pUSD balance once\n/// platform and builder taker fees are deducted. Mirrors `adjust_market_buy_amount`\n/// in `polymarket-rs-clob-client-v2`'s `clob/utilities.rs`.\n///\n/// Returns `amount` unchanged when the balance already covers `amount + fees`.\n/// Otherwise solves for the principal that, with fees, exactly consumes the\n/// balance, then truncates to `USDC_DECIMALS` (the on-chain pUSD scale).\n///\n/// The fee-curve step `(p * (1 - p))^exponent` is the only computation that\n/// crosses into `f64`, matching the reference SDK so we agree with the","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/execution/parse.rs#L460-L496","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (fixture)\n{\"maker\": {\"exponent\": {\"value\": 2}}}\n// after\n{\"maker\": {\"exponent\": 2}}","handlingStrategy":"type-guard","validationCode":"fn exponent_is_valid(schedule: &serde_json::Value) -> bool {\n    matches!(\n        schedule.get(\"exponent\"),\n        Some(serde_json::Value::String(_) | serde_json::Value::Number(_))\n    )\n}","typeGuard":"fn as_exponent(value: &serde_json::Value) -> Option<&serde_json::Value> {\n    match value.get(\"exponent\")? {\n        v @ (serde_json::Value::String(_) | serde_json::Value::Number(_)) => Some(v),\n        _ => None,\n    }\n}","tryCatchPattern":"match build_fill_reports_from_trades(&trades, &fee_schedule) {\n    Err(e) if e.to_string().contains(\"fee exponent must be a decimal\") => {\n        error!(\"malformed fee schedule JSON: {e:#}; dump raw payload for diagnosis\");\n    }\n    Err(e) => return Err(e),\n    Ok(reports) => {},\n}","preventionTips":["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"],"tags":["polymarket","json","fee-parsing","schema"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}