nautechsystems/nautilus_trader · error · anyhow::Error
serde_json deserialization error: {e}
Error message
serde_json deserialization error: {e} What it means
After a successful products fetch, load_from_products_response deserializes the raw JSON into the typed ProductsResponse model with serde_json::from_value. If the JSON does not match the expected schema (missing required fields, wrong types, unexpected nulls), serde's error is re-raised as this anyhow error. It means the response was received but its shape did not fit the adapter's model.
Source
Thrown at crates/adapters/coinbase/src/provider.rs:130
.await
.map_err(|e| anyhow::anyhow!("Failed to fetch product '{product_id}': {e}"))?;
self.load_from_product_response(&json)
}
/// Parses a products list response and caches the instruments.
///
/// Expects the JSON shape returned by `GET /products`: `{"products": [...]}`.
///
/// # Errors
///
/// Returns an error if the JSON cannot be deserialized or any product fails to parse.
pub fn load_from_products_response(
&self,
json: &serde_json::Value,
) -> anyhow::Result<Vec<InstrumentAny>> {
let response: ProductsResponse =
serde_json::from_value(json.clone()).map_err(|e| anyhow::anyhow!("{e}"))?;
let instruments = self.parse_and_cache_products(&response.products)?;
// Populate the alias map so subscribe paths (which only see the parsed
// `InstrumentAny`) can resolve a caller-supplied product id back to the
// canonical id Coinbase uses on the wire.
self.client.record_product_aliases(&response.products);
Ok(instruments)
}
/// Parses a products list response, filtering by product type, and caches the instruments.
///
/// # Errors
///
/// Returns an error if the JSON cannot be deserialized or any product fails to parse.
pub fn load_from_products_response_filtered(
&self,
json: &serde_json::Value,
product_type: CoinbaseProductType,View on GitHub (pinned to 18893faf8b)
Solutions
- Log/inspect serde's message ({e}) to identify the exact field and expected type.
- Compare the failing JSON against the adapter's ProductsResponse/Product struct definitions.
- Update the model structs or add serde defaults/skippable attributes if the Coinbase schema changed.
- Ensure the value passed in is a full products-list response, not a single product or error body.
Example fix
// before
let v: serde_json::Value = serde_json::from_str(raw)?;
let instruments = provider.load_from_products_response(&v)?;
// after — guard shape first
let v: serde_json::Value = serde_json::from_str(raw)?;
anyhow::ensure!(v.get("products").map_or(false, |p| p.is_array()), "not a products response");
let instruments = provider.load_from_products_response(&v)?; Defensive patterns
Strategy: type-guard
Validate before calling
fn looks_like_products_response(v: &serde_json::Value) -> bool {
v.get("products").map_or(false, |p| p.is_array())
}
anyhow::ensure!(looks_like_products_response(&json), "payload is not a products response"); Type guard
fn is_products_response(v: &serde_json::Value) -> bool { v.get("products").map_or(false, serde_json::Value::is_array) } Try / catch
match serde_json::from_value::<ProductsResponse>(json.clone()) {
Ok(resp) => process(resp),
Err(e) => { error!("schema mismatch: {e}"); log_raw_payload(&json); }
} Prevention
- Log the raw payload whenever deserialization fails to spot schema drift quickly.
- Add round-trip fixture tests that deserialize real Coinbase responses.
- Pin/track Coinbase API versions and review changelogs for product schema changes.
When it happens
Trigger: Calling load_from_products_response(json) (directly in tests/processors, or via load_all) with a JSON value missing fields the Product struct requires, with incompatible types (string vs number), or from a Coinbase API version whose schema changed.
Common situations: Coinbase adding/removing fields and the adapter's structs lagging behind; hand-crafted or recorded fixture JSON used in tests; passing a single-product JSON object where a products-list response is expected.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- serde_json deserialization error: {e}
- Failed to deserialize instrument payload
- Failed to deserialize record: {e}
- Failed to convert value to target type: {e}
- Failed to decode position replay state: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/363db48f5a3206e9.
Report an issue: GitHub.