Hmbown/CodeWhale · error

roundtrip

Error message

roundtrip

What it means

The same test then parses the JSON back with `serde_json::from_str::<CatalogCache>(&json).expect("roundtrip")`, panicking if deserialization fails. A roundtrip failure means the serialized shape no longer matches `CatalogCache`'s `Deserialize` expectations — field type drift, missing `#[serde(default)]`, or an incompatible custom deserializer.

Solutions

  1. Make new fields optional with `#[serde(default)]` or add a cache format version
  2. Diff the serialized JSON against `CatalogCache`'s field set and types
  3. Remove `deny_unknown_fields` or align the custom Deserialize with the Serialize output
  4. Print the serde error (it names the field/path) with `unwrap_or_else(|e| panic!("{e}"))`

Example fix

// before
let parsed: CatalogCache = serde_json::from_str(&json).expect("roundtrip");
// after
let parsed: CatalogCache = serde_json::from_str(&json)
    .unwrap_or_else(|e| panic!("cache roundtrip failed: {e}\njson={json}"));
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the serialized JSON parses before full roundtrip assertions
if let Err(e) = serde_json::from_str::<CatalogCache>(&json) {
    panic!("roundtrip pre-check failed at {}: {e}", e);
}

Try / catch

let parsed: CatalogCache = serde_json::from_str(&json)
    .unwrap_or_else(|e| panic!("CatalogCache roundtrip failed: {e}\njson={json}"));

Prevention

When it happens

Trigger: Adding a required field to `CatalogCache` that old serialized JSON lacks; changing a field type (e.g. `u64` → enum) without migration; a custom `Deserialize` that rejects the output of the current `Serialize` impl.

Common situations: Cache-format evolution without a version field or serde defaults; hand-editing the JSON in another test fixture; enabling `deny_unknown_fields` while serialization emits extra fields.

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 Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/e190f7a52681bea5. Report an issue: GitHub.

Appendix: source

Thrown at crates/models/src/model_catalog.rs:374

        let mut entries = BTreeMap::new();
        entries.insert(
            "sample/model".to_string(),
            CatalogEntry {
                input_usd_per_million: Some(0.25),
                output_usd_per_million: Some(1.25),
                ..entry("sample/model", 32_000, MetadataProvenance::ProviderApi)
            },
        );
        let cache = cache(Utc::now(), 60, entries);
        let json = serde_json::to_string_pretty(&cache).expect("serialize");
        let lowered = json.to_lowercase();
        for forbidden in ["api_key", "authorization", "token", "secret"] {
            assert!(
                !lowered.contains(forbidden),
                "cache JSON must not contain auth field {forbidden}: {json}"
            );
        }
        let parsed: CatalogCache = serde_json::from_str(&json).expect("roundtrip");
        assert_eq!(parsed.entries.len(), 1);
    }
}

View on GitHub (pinned to 433685b202)