nautechsystems/nautilus_trader · error · anyhow::Error

missing fee for fill report inst_id={}

Error message

missing fee for fill report inst_id={}

What it means

OKX sends the fee on every order fill, and the adapter requires it to build the fill report's commission (Money). This error is thrown when the order message's fee field is absent (None) or whitespace-only, so there is no fee string to parse and the fill report is rejected rather than emitted with a zero/unknown fee.

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:2151

                current_filled
            }
        } else {
            anyhow::bail!(
                "Cannot determine fill quantity: fill_sz is empty/zero and acc_fill_sz is empty/zero"
            );
        }
    } else {
        anyhow::bail!(
            "Cannot determine fill quantity: fill_sz='{}' and acc_fill_sz is None",
            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!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Filter order updates so only messages with actual fill data (fillSz/accFillSz changed) reach parse_fill_report; canceled orders typically carry no fee.
  2. Check the raw payload for the given inst_id to confirm fee is genuinely missing from the exchange push.
  3. If OKX legitimately stopped sending fee on fills for your account tier, update the adapter to fetch fees from the REST fills endpoint or emit the fill without commission.
  4. Keep the adapter updated to the OKX API version you are targeting.

Example fix

// before: feeding every order update to parse_fill_report
let report = parse_fill_report(&msg, ...)?;
// after: only fill-bearing updates
if msg.fill_sz.is_empty() && msg.acc_fill_sz.map_or(true, |s| s == "0") { return Ok(None); }
let report = parse_fill_report(&msg, ...)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: ensure fee is present before requesting a fill report
fn has_fee(fee: &Option<String>) -> bool {
    fee.as_deref().map_or(false, |f| !f.trim().is_empty())
}

Type guard

fn fee_present(msg: &OkxOrderMsg) -> Option<&str> {
    msg.fee.as_deref().filter(|f| !f.trim().is_empty())
}

Try / catch

match parse_fill_report(&msg, ...) {
    Ok(r) => emit(r),
    Err(e) if e.to_string().contains("missing fee") => {
        log::debug!("no fee on update for {} (likely non-fill push); skipping", msg.inst_id);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: parse_fill_report receives an OKX 'orders' message whose fee: Option<String> is None or Some(""/whitespace). The error message includes the msg.inst_id of the offending fill.

Common situations: OKX sometimes omits fee on certain order-state pushes (e.g. canceled or newly-created order updates with no fill); using a message variant/channel where fee is not populated; OKX API change removing the field on some fill events.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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