nautechsystems/nautilus_trader · error · anyhow::Error
unsupported linear contract variant: {other:?}
Error message
unsupported linear contract variant: {other:?} What it means
parse_linear_instrument matches on BybitContractType to decide whether a linear instrument is a LinearPerpetual (CryptoPerpetual) or LinearFutures (CryptoFuture). Any other contract-type variant returned by the exchange hits a catch-all arm that returns this error. It signals an exchange contract type the adapter does not know how to map to a Nautilus instrument type.
Source
Thrown at crates/adapters/bybit/src/common/parse.rs:551
.size_increment(size_increment)
.maybe_lot_size(lot_size)
.maybe_max_quantity(max_quantity)
.maybe_min_quantity(min_quantity)
.maybe_min_notional(min_notional)
.maybe_max_price(max_price)
.maybe_min_price(min_price)
.margin_init(default_margin())
.margin_maint(default_margin())
.maker_fee(maker_fee)
.taker_fee(taker_fee)
.maybe_info(info)
.ts_event(ts_event)
.ts_init(ts_init)
.build()
.unwrap();
Ok(InstrumentAny::CryptoFuture(instrument))
}
other => Err(anyhow::anyhow!(
"unsupported linear contract variant: {other:?}"
)),
}
}
/// Parses Bybit's `minNotionalValue` string (when present) into a `Money` value
/// denominated in the instrument's quote currency. Returns `Ok(None)` if the
/// field is absent or an empty string.
fn parse_optional_notional(
raw: Option<&str>,
currency: Currency,
field: &str,
) -> anyhow::Result<Option<Money>> {
let Some(s) = raw.map(str::trim).filter(|s| !s.is_empty()) else {
return Ok(None);
};
let amount: f64 = s
.parse()View on GitHub (pinned to 18893faf8b)
Solutions
- Upgrade the adapter to a version supporting the new BybitContractType variant
- Filter out symbols with unsupported contractType before calling parse_linear_instrument
- Extend the match in parse_linear_instrument to map or explicitly skip the new variant
- Check which symbol/contractType triggered it (the Debug output in the message) and exclude that category
Example fix
// before
other => Err(anyhow::anyhow!("unsupported linear contract variant: {other:?}")),
// after
other => {
tracing::debug!(?other, "skipping unsupported linear contract variant");
Ok(None)
} Defensive patterns
Strategy: type-guard
Validate before calling
fn is_supported_linear(ct: &BybitContractType) -> bool {
matches!(ct, BybitContractType::LinearPerpetual | BybitContractType::LinearFutures)
} Type guard
fn as_supported_linear(ct: &BybitContractType) -> Option<&BybitContractType> {
matches!(ct, BybitContractType::LinearPerpetual | BybitContractType::LinearFutures).then_some(ct)
} Try / catch
match parse_linear_instrument(&def, &fee, ts_event, ts_init) {
Ok(inst) => instruments.push(inst),
Err(e) if e.to_string().contains("unsupported linear contract variant") => {
tracing::debug!(symbol = %def.symbol, "skipping unsupported contract");
}
Err(e) => return Err(e),
} Prevention
- Filter by contractType before routing symbols to parsers
- Keep the adapter updated for new Bybit contract types
- Log (Debug) unknown variants rather than failing batch requests
- Add exhaustive-match tests covering every BybitContractType variant
When it happens
Trigger: Bybit's instruments-info response contains a contractType value (e.g. a newly introduced product line) that deserializes into a BybitContractType variant outside LinearPerpetual/LinearFutures, and the symbol is routed through parse_linear_instrument.
Common situations: Bybit adding new contract types on v5 API (e.g. options-like or pre-market contracts appearing in the linear list); running an older adapter version against an updated exchange API; filtered category='linear' requests returning unexpected instruments.
Related errors
- unsupported inverse contract variant: {other:?}
- base_coin is empty for symbol '{}'
- quote_coin is empty for symbol '{}'
- Invalid BybitPositionMode value: {int_val}
- Invalid BybitPositionIdx value: {int_val}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5f39683780e86c43.
Report an issue: GitHub.