nautechsystems/nautilus_trader · warning
empty funding_rate for {instrument_id} (dated futures do not
Error message
empty funding_rate for {instrument_id} (dated futures do not have funding rates) What it means
Bybit's linear ticker payload contained an empty `funding_rate` string. For dated (delivery) futures Bybit sends an empty funding rate because those instruments have no funding; the adapter surfaces this explicitly rather than parsing an empty string into a bogus rate.
Source
Thrown at crates/adapters/bybit/src/websocket/parse.rs:467
/// Parses a linear ticker payload into a [`FundingRateUpdate`].
///
/// # Errors
///
/// Returns an error if funding rate, funding interval or next funding time fields are missing or cannot be parsed.
pub fn parse_ticker_linear_funding(
data: &BybitWsTickerLinear,
instrument_id: InstrumentId,
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> anyhow::Result<FundingRateUpdate> {
let funding_rate_str = data
.funding_rate
.as_ref()
.context("Bybit ticker missing funding_rate")?;
if funding_rate_str.is_empty() {
anyhow::bail!(
"empty funding_rate for {instrument_id} (dated futures do not have funding rates)"
);
}
let funding_rate = funding_rate_str
.as_str()
.parse::<Decimal>()
.with_context(|| {
format!("invalid funding_rate value '{funding_rate_str}' for {instrument_id}")
})?;
let funding_interval = if let Some(funding_interval_hour) = &data.funding_interval_hour {
let funding_interval_hour = funding_interval_hour
.as_str()
.parse::<u16>()
.context("invalid funding_interval_hour value")?;
Some(
funding_interval_hourView on GitHub (pinned to 18893faf8b)
Solutions
- Only request funding rates for perpetual instruments; skip dated futures tickers before parsing.
- Treat the error as 'instrument has no funding' and continue processing the stream instead of failing.
- Check `instrument_info`/contract type when subscribing so delivery contracts are excluded from funding handling.
Example fix
// before: parse every linear ticker
let fr = parse_ticker_linear_funding(&data, instrument_id)?;
// after: skip dated futures
if !is_perpetual(instrument_id) {
return Ok(None); // dated futures have no funding rate
}
let fr = parse_ticker_linear_funding(&data, instrument_id)?; Defensive patterns
Strategy: validation
Validate before calling
if !instrument_id.to_string().contains("-PERP") {
return Ok(None); // dated futures have no funding rate
} Type guard
fn is_perpetual(instrument_id: &InstrumentId) -> bool {
instrument_id.symbol.to_string().ends_with("-PERP")
} Try / catch
match parse_ticker_linear_funding(&data, instrument_id) {
Ok(fr) => publish_funding(fr),
Err(e) if e.to_string().contains("empty funding_rate") => {
tracing::debug!("{instrument_id} is dated; no funding rate");
}
Err(e) => return Err(e),
} Prevention
- Subscribe to funding tickers only for perpetual instruments
- Filter delivery/dated contracts out of funding consumers
- Check instrument metadata (contract type) at subscription time
When it happens
Trigger: `parse_ticker_linear_funding` is called on a ticker message for a dated/delivery futures instrument whose `funding_rate` field is `Some("")`.
Common situations: Subscribing to tickers on dated futures (e.g. quarterly expiries) where funding does not apply; running a perp-focused consumer against delivery contracts.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- funding_interval_hour out of bounds
- WS submit order failed: {e}
- WS cancel order failed: {e}
- Binance Futures shutdown failed: {}
- errors.join("; ")
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/757dfdd12ba9c314.
Report an issue: GitHub.