{"record":{"id":"1e303ccaf0d972dc","repo":"nautechsystems/nautilus_trader","slug":"failed-to-fetch-product-product-id-e","errorCode":null,"errorMessage":"Failed to fetch product '{product_id}': {e}","messagePattern":"Failed to fetch product '(.+?)': (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/coinbase/src/http/client.rs","lineNumber":1072,"sourceCode":"        self.cache_instruments(&instruments);\n        self.record_product_aliases(&response.products);\n        Ok(instruments)\n    }\n\n    /// Requests a single instrument by product ID.\n    ///\n    /// Caches the result on success.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error when the HTTP request fails, deserialization fails,\n    /// or the product cannot be parsed into a supported instrument.\n    pub async fn request_instrument(&self, product_id: &str) -> anyhow::Result<InstrumentAny> {\n        let json = self\n            .inner\n            .get_product(product_id)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to fetch product '{product_id}': {e}\"))?;\n        let product: crate::http::models::Product =\n            serde_json::from_value(json).map_err(|e| anyhow::anyhow!(e))?;\n        let ts_init = self.ts_now();\n        let instrument = parse_instrument(&product, ts_init)?;\n        self.cache_instrument(&instrument);\n        self.record_product_aliases(std::slice::from_ref(&product));\n        Ok(instrument)\n    }\n\n    /// Requests the raw product payload for a product ID.\n    ///\n    /// Returns the full [`crate::http::models::Product`] so callers can read\n    /// derivatives-specific fields (`future_product_details.index_price`,\n    /// `funding_rate`, `funding_time`) that are stripped when parsing to a\n    /// Nautilus instrument.\n    ///\n    /// # Errors\n    ///","sourceCodeStart":1054,"sourceCodeEnd":1090,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/coinbase/src/http/client.rs#L1054-L1090","documentation":"Raised by request_instrument when get_product(product_id) fails for the given product. The HTTP client error is wrapped with the offending product_id, so the caller knows which instrument lookup failed before any parsing occurs.","triggerScenarios":"Calling request_instrument(product_id) where the REST GET /products/{id} call fails: unknown product id (404), network error, rate limit, or auth problem.","commonSituations":"Typo'd or delisted product id (e.g. 'BTC-USD' vs 'BTC-USDC'), instruments that no longer exist on Coinbase, temporary network/API issues during bootstrap, callers like get_or_fetch_instrument resolving cache misses.","solutions":["Verify the product_id exists on Coinbase Advanced Trade (use request_instruments to list valid ids).","Check the wrapped error for 404 (invalid/delisted product) vs 5xx (retry with backoff).","Retry transient failures with exponential backoff.","Pre-populate the instrument cache via request_instruments so get_or_fetch_instrument does not hit this path."],"exampleFix":"// before\nlet inst = client.request_instrument(\"BTC-USD\").await?;\n// after (validate id first)\nlet valid = client.request_instruments(None).await?;\nanyhow::ensure!(valid.iter().any(|i| i.id().as_str() == \"BTC-USD\"), \"unknown product\");\nlet inst = client.request_instrument(\"BTC-USD\").await?;","handlingStrategy":"validation","validationCode":"let known: Vec<InstrumentAny> = client.request_instruments(None).await?;\nanyhow::ensure!(\n    known.iter().any(|i| i.id().as_str() == product_id),\n    \"product {product_id} not listed on Coinbase\"\n);","typeGuard":null,"tryCatchPattern":"match client.request_instrument(product_id).await {\n    Ok(i) => i,\n    Err(e) if e.to_string().contains(\"404\") => bail!(\"delisted/unknown product {product_id}\"),\n    Err(e) => { backoff_retry(|| client.request_instrument(product_id)).await? }\n}","preventionTips":["Validate product ids against the /products list before single-instrument lookups.","Cache instruments up front so get_or_fetch_instrument avoids network calls.","Distinguish 404 (fatal) from 5xx/429 (retryable) when handling the error."],"tags":["http","coinbase","instrument","rest-api"],"backgroundTag":"http-request-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}