nautechsystems/nautilus_trader · error

custom data request requires `instrument_id` metadata

Error message

custom data request requires `instrument_id` metadata

What it means

A Binance Futures custom data request (DataCmd) must include `instrument_id` in its metadata map, so the adapter can resolve which instrument the request targets. `required_instrument_id_metadata` bails when the metadata is absent, empty, or whitespace.

Solutions

  1. Add `instrument_id` to the custom data request metadata as a non-empty string
  2. Ensure the value is a valid InstrumentId parseable by the adapter (e.g. BTCUSDT-PERP.BINANCE)
  3. Trim whitespace from the configured value
  4. Fix surrounding code if the metadata is a serde map being silently dropped (wrong serialization type)

Example fix

// before
let data_type = DataType::new("BinanceFuturesOpenInterestHist", None);
// after
let metadata = indexmap::IndexMap::from([("instrument_id".to_string(), serde_json::json!("BTCUSDT-PERP.BINANCE"))]);
let data_type = DataType::new("BinanceFuturesOpenInterestHist", Some(metadata));
Defensive patterns

Strategy: validation

Validate before calling

let id = data_type.metadata()
    .and_then(|m| m.get("instrument_id"))
    .and_then(|v| v.as_str())
    .map(str::trim)
    .filter(|s| !s.is_empty());
assert!(id.is_some(), "instrument_id metadata required");

Type guard

fn has_instrument_id(meta: &Option<IndexMap<String, serde_json::Value>>) -> bool {
    meta.as_ref().and_then(|m| m.get("instrument_id")).and_then(|v| v.as_str()).map(str::trim).map_or(false, |s| !s.is_empty())
}

Try / catch

match client.request_data(data_type).await {
    Err(e) if e.to_string().contains("requires `instrument_id` metadata") => return Err(anyhow::anyhow!("add instrument_id metadata to request")),
    other => other,
}

Prevention

When it happens

Trigger: Calling request_data with a custom data type (e.g. BinanceFuturesOpenInterestHist) whose DataType metadata either has no `instrument_id` key, has a non-string value, or a blank/whitespace string.

Common situations: Building a DataCmd programmatically and forgetting the metadata entry, copying example code without the metadata map, or passing an ID wrapped in quotes/whitespace.

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/96ae69b0f96d6adb. Report an issue: GitHub.

Appendix: source

Thrown at crates/adapters/binance/src/futures/data.rs:476

            return Ok(None);
        };

        let instrument_id = InstrumentId::from_str(raw_instrument_id)
            .with_context(|| format!("invalid instrument_id metadata `{raw_instrument_id}`"))?;

        Ok(Some(instrument_id))
    }

    fn required_instrument_id_metadata(data_type: &DataType) -> anyhow::Result<InstrumentId> {
        let Some(raw_instrument_id) = data_type
            .metadata()
            .as_ref()
            .and_then(|m| m.get("instrument_id"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|value| !value.is_empty())
        else {
            anyhow::bail!("custom data request requires `instrument_id` metadata");
        };

        InstrumentId::from_str(raw_instrument_id)
            .with_context(|| format!("invalid instrument_id metadata `{raw_instrument_id}`"))
    }

    fn required_period_metadata(data_type: &DataType) -> anyhow::Result<String> {
        let Some(period) = data_type
            .metadata()
            .as_ref()
            .and_then(|m| m.get("period"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|value| !value.is_empty())
        else {
            anyhow::bail!("historical open interest request requires `period` metadata");
        };

View on GitHub (pinned to 18893faf8b)