{"record":{"id":"e190f7a52681bea5","repo":"Hmbown/CodeWhale","slug":"roundtrip","errorCode":null,"errorMessage":"roundtrip","messagePattern":"roundtrip","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/models/src/model_catalog.rs","lineNumber":374,"sourceCode":"        let mut entries = BTreeMap::new();\n        entries.insert(\n            \"sample/model\".to_string(),\n            CatalogEntry {\n                input_usd_per_million: Some(0.25),\n                output_usd_per_million: Some(1.25),\n                ..entry(\"sample/model\", 32_000, MetadataProvenance::ProviderApi)\n            },\n        );\n        let cache = cache(Utc::now(), 60, entries);\n        let json = serde_json::to_string_pretty(&cache).expect(\"serialize\");\n        let lowered = json.to_lowercase();\n        for forbidden in [\"api_key\", \"authorization\", \"token\", \"secret\"] {\n            assert!(\n                !lowered.contains(forbidden),\n                \"cache JSON must not contain auth field {forbidden}: {json}\"\n            );\n        }\n        let parsed: CatalogCache = serde_json::from_str(&json).expect(\"roundtrip\");\n        assert_eq!(parsed.entries.len(), 1);\n    }\n}\n","sourceCodeStart":356,"sourceCodeEnd":378,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/models/src/model_catalog.rs#L356-L378","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make new fields optional with `#[serde(default)]` or add a cache format version","Diff the serialized JSON against `CatalogCache`'s field set and types","Remove `deny_unknown_fields` or align the custom Deserialize with the Serialize output","Print the serde error (it names the field/path) with `unwrap_or_else(|e| panic!(\"{e}\"))`"],"exampleFix":"// before\nlet parsed: CatalogCache = serde_json::from_str(&json).expect(\"roundtrip\");\n// after\nlet parsed: CatalogCache = serde_json::from_str(&json)\n    .unwrap_or_else(|e| panic!(\"cache roundtrip failed: {e}\\njson={json}\"));","handlingStrategy":"validation","validationCode":"// Confirm the serialized JSON parses before full roundtrip assertions\nif let Err(e) = serde_json::from_str::<CatalogCache>(&json) {\n    panic!(\"roundtrip pre-check failed at {}: {e}\", e);\n}","typeGuard":null,"tryCatchPattern":"let parsed: CatalogCache = serde_json::from_str(&json)\n    .unwrap_or_else(|e| panic!(\"CatalogCache roundtrip failed: {e}\\njson={json}\"));","preventionTips":["Add `#[serde(default)]` to new optional cache fields so old JSON still parses","Version the cache format (`version: u32`) and reject/upgrade mismatches explicitly","Never mix `deny_unknown_fields` with forward-evolving cache files"],"tags":["rust","serde","json","deserialization"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T10:30:35.592Z"}