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

  1. Log/inspect serde's message ({e}) to identify the exact field and expected type.
  2. Compare the failing JSON against the adapter's ProductsResponse/Product struct definitions.
  3. Update the model structs or add serde defaults/skippable attributes if the Coinbase schema changed.
  4. 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

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


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