nautechsystems/nautilus_trader · error · anyhow::Error
Failed to parse fee={:?}: {}
Error message
Failed to parse fee={:?}: {} What it means
The adapter parses the fee string into a Money amount (parse_fee negates OKX's negative-sign convention so charges become positive). This error is thrown when parse_fee fails even though the raw Decimal conversion succeeded — typically the fee currency could not be resolved or the value is out of the allowed range for Money.
Source
Thrown at crates/adapters/okx/src/websocket/parse.rs:2161
msg.fill_sz
);
};
let fee_str = msg
.fee
.as_deref()
.filter(|fee| !fee.trim().is_empty())
.ok_or_else(|| anyhow::anyhow!("missing fee for fill report inst_id={}", msg.inst_id))?;
let fee_dec = Decimal::from_str(fee_str)
.map_err(|e| anyhow::anyhow!("Failed to parse fee '{fee_str}': {e}"))?;
let fee_currency = parse_fee_currency(msg.fee_ccy.as_str(), fee_dec, || {
format!("fill report for inst_id={}", msg.inst_id)
});
// OKX sends fees as negative numbers (e.g., "-2.5" for a $2.5 charge), parse_fee negates to positive
let total_fee = parse_fee(Some(fee_str), fee_currency)
.map_err(|e| anyhow::anyhow!("Failed to parse fee={:?}: {}", msg.fee, e))?;
// OKX sends cumulative fees, so we subtract the previous total to get this fill's fee
let commission = if let Some(previous_fee) = previous_fee {
if total_fee.currency == previous_fee.currency {
let incremental = total_fee - previous_fee;
if incremental < Money::zero(fee_currency) {
log::debug!(
"Negative incremental fee detected - likely a maker rebate or fee refund: order_id={}, total_fee={}, previous_fee={}, incremental={}",
msg.ord_id.as_str(),
total_fee,
previous_fee,
incremental,
);
}
// Skip corruption check when previous is negative (rebate), as transitions from
// rebate to charge legitimately have incremental > total (e.g., -1 → +2 gives +3)View on GitHub (pinned to 18893faf8b)
Solutions
- Register the fee currency (fee_ccy) in the local currency/currency-registry used by parse_fee_currency and parse_fee.
- Log msg.fee and msg.fee_ccy for the failing fill and compare against the currencies the adapter knows.
- Update the adapter's currency mapping if OKX introduced new fee currencies.
- Report upstream if the fee value is valid but Money construction still fails (precision bug in the adapter).
Defensive patterns
Strategy: type-guard
Validate before calling
// Rust: ensure fee_ccy is a known currency before processing fills
fn fee_currency_known(ccy: &str, registry: &CurrencyRegistry) -> bool {
registry.contains(ccy)
} Type guard
fn known_fee_ccy<'a>(msg: &'a OkxOrderMsg, registry: &CurrencyRegistry) -> Option<&'a str> {
registry.contains(&msg.fee_ccy).then_some(msg.fee_ccy.as_str())
} Try / catch
match parse_fill_report(&msg, ...) {
Ok(r) => emit(r),
Err(e) if e.to_string().contains("Failed to parse fee=") => {
register_currency(&msg.fee_ccy); // then retry once
}
Err(e) => return Err(e),
} Prevention
- Register every fee currency you may be charged in (quote ccys of traded instruments).
- Keep the currency registry updated with OKX listings.
- Log fee/fee_ccy pairs on failure to spot new currencies fast.
When it happens
Trigger: parse_fill_report: parse_fee(Some(fee_str), fee_currency) returns Err — e.g. a precision/currency issue in the fee_currency derived from fee_ccy, or a value that cannot be represented as Money at the currency's precision.
Common situations: fee_ccy from OKX is a currency unknown to the local currency registry (new listing, unusual quote ccy); fee magnitude beyond Money precision; mismatch between the decimal precision of the fee and the currency's precision.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- missing fee for fill report inst_id={}
- Failed to parse spread fill_sz='{}': {e}
- Failed to parse spread acc_fill_sz='{}': {e}
- Failed to parse price (fill_px='{}', avg_px='{}', px='{}'):
- Failed to parse fill_sz='{}': {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9e0fdb63c392511b.
Report an issue: GitHub.