nautechsystems/nautilus_trader · error
Cannot determine fill quantity: fill_sz is empty/zero and ac
Error message
Cannot determine fill quantity: fill_sz is empty/zero and acc_fill_sz is empty/zero
What it means
A fill quantity can only be derived from fill_sz (incremental) or acc_fill_sz (cumulative). When both are empty or zero the parser cannot produce a meaningful fill and bails, since emitting a zero fill would be ambiguous.
Source
Thrown at crates/adapters/okx/src/websocket/parse.rs:2136
anyhow::bail!(
"Cumulative fill went backwards: acc_fill_sz='{acc_fill_sz}' < previous_filled_qty={prev_qty} \
(possible stale data after reconnect)"
);
}
let incremental = current_filled - prev_qty;
if incremental.is_zero() {
log::debug!(
"Skipping duplicate fill: acc_fill_sz='{acc_fill_sz}' unchanged from previous={prev_qty}"
);
return Ok(None);
}
incremental
} else {
// First fill, use accumulated as incremental
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}"))?;
View on GitHub (pinned to 18893faf8b)
Solutions
- Only route updates with status 'live'/'partially_filled' and a nonzero fill into parse_fill_report; treat others as status events
- Return None instead of an error for zero-size updates if your flow expects them
- Log the raw message and confirm which OKX channel it came from
Example fix
// before
let fill = parse_fill_report(&msg, instrument, prev, ...)?; // bails on zero sizes
// after
if msg.fill_sz.is_none() && msg.acc_fill_sz.is_none() {
return Ok(None); // status-only update
} Defensive patterns
Strategy: type-guard
Validate before calling
fn is_zero_fill(msg: &OKXOrderMsg) -> bool {
let zero = |s: Option<&String>| s.map_or(true, |v| v.is_empty() || v == "0");
zero(msg.fill_sz.as_ref()) && zero(msg.acc_fill_sz.as_ref())
} Type guard
fn has_fill_data(msg: &OKXOrderMsg) -> bool { !is_zero_fill(msg) } Try / catch
if is_zero_fill(&msg) { return Ok(None); } // status-only update, not a fill Prevention
- Only call parse_fill_report for statuses that imply fills (live/partially_filled)
- Keep test fixtures consistent with real OKX payloads
- Handle status-only updates as OrderStatusReports, not fills
When it happens
Trigger: An orders-channel update where fill_sz is empty or '0' and acc_fill_sz is also empty or zero — e.g. a status-only update (canceled/amended) routed through parse_fill_report, or a first snapshot with no fills yet.
Common situations: Order state changes (cancel/modify) delivered on the same channel and mistaken for fills; malformed fixtures; OKX field semantics differences between channels.
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
- Missing sz for algo order {}
- Cannot determine spread fill quantity: fill_sz='{}' and acc_
- Cannot determine fill quantity: fill_sz='{}' and acc_fill_sz
- Unsupported algo order type: {:?}
- missing fee for spread fill report sprd_id={}; OKX sprd-orde
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e299f15529774599.
Report an issue: GitHub.