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
- Filter Coinbase products before loading, keeping only supported crypto spot products
- Check the product's product_type in the raw JSON and skip unsupported ones
- Update the adapter's `is_supported_product` if a new legitimately supported product type was added upstream
- 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
- Request/filter only spot products from the Coinbase products endpoint
- Log and skip unsupported product types instead of treating them as fatal
- Pin or review the Coinbase API version so product_type enums stay stable
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
- Failed to fetch product '{product_id}': {e}
- Binance Futures position has unresolved instrument {instrume
- Unsupported minute step: {step}
- Unsupported hour step: {step}
- Unsupported day step: {step}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/13fc8881bfe9a981.
Report an issue: GitHub.