nautechsystems/nautilus_trader · error · anyhow::Error

Unsupported product '{product_id}' (type={product_type}, non

Error message

Unsupported product '{product_id}' (type={product_type}, non_crypto={non_crypto})

What it means

Coinbase instrument loading rejects products that fail `is_supported_product` during deserialization from the /products REST response. The adapter only supports crypto spot products; futures and other non-crypto product types are intentionally not converted into Nautilus instruments. The message includes product id, type, and the future_product_details flag to pinpoint why it was rejected.

Source

Thrown at crates/adapters/coinbase/src/provider.rs:181

        self.client.record_product_aliases(&response.products);
        Ok(instruments)
    }

    /// Parses a single product response and caches the instrument.
    ///
    /// Expects the JSON shape returned by `GET /products/{product_id}`.
    ///
    /// # Errors
    ///
    /// Returns an error if the JSON cannot be deserialized or the product fails to parse.
    pub fn load_from_product_response(
        &self,
        json: &serde_json::Value,
    ) -> anyhow::Result<InstrumentAny> {
        let product: Product =
            serde_json::from_value(json.clone()).map_err(|e| anyhow::anyhow!("{e}"))?;

        anyhow::ensure!(
            is_supported_product(&product),
            "Unsupported product '{}' (type={}, non_crypto={})",
            product.product_id,
            product.product_type,
            product
                .future_product_details
                .as_ref()
                .is_some_and(|d| d.non_crypto),
        );

        let ts_init = self.client.ts_now();
        let instrument = parse_instrument(&product, ts_init)?;

        self.cache_instrument(&instrument);
        self.client
            .record_product_aliases(std::slice::from_ref(&product));

        Ok(instrument)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Filter Coinbase products before loading, keeping only supported crypto spot products
  2. Check the product's product_type in the raw JSON and skip unsupported ones
  3. Update the adapter's `is_supported_product` if a new legitimately supported product type was added upstream
  4. Confirm you are using the spot Coinbase API/endpoint rather than one returning futures

Example fix

// before
let instrument = provider.load_from_product_response(&product_json)?;
// after
if !product_json["future_product_details"].is_null() {
    log::warn!("skipping non-spot product: {}", product_json["product_id"]);
    continue;
}
let instrument = provider.load_from_product_response(&product_json)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_spot(product: &serde_json::Value) -> bool {
    product["future_product_details"].is_null()
        && product["product_type"] == "SPOT"
}

Type guard

fn as_supported_product(json: &serde_json::Value) -> Option<&serde_json::Value> {
    if json["future_product_details"].is_null() { Some(json) } else { None }
}

Try / catch

match provider.load_from_product_response(&json) {
    Ok(instrument) => cache.add(instrument),
    Err(e) if e.to_string().contains("Unsupported product") => log::debug!("skipped: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `load` / `load_from_product_response` with a Coinbase product JSON whose product_type is not a supported crypto type (e.g. FUTURE), or whose `future_product_details` is populated.

Common situations: Pointing the adapter at Coinbase products that include futures (e.g. PERP- or EXP- prefixed products like BTC-PERP-...), a Coinbase API version change adding new product types, or loading the full product universe instead of filtering to spot products.

Related errors


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