Hmbown/CodeWhale · error

serialize

Error message

serialize

What it means

`cache_roundtrip_serializes_no_secret_fields` calls `serde_json::to_string_pretty(&cache).expect("serialize")`, panicking if `CatalogCache` fails to serialize to JSON. With serde this essentially only fails if a field's serializer errors (e.g. a non-string key map or a custom Serialize returning Err) — normally it cannot fail, so the expect is a belt-and-braces guard in the test.

Solutions

  1. Check any custom `Serialize` impls added to `CatalogCache`/`ModelEntry` for `Err` returns
  2. Ensure all serialized fields are JSON-compatible (string map keys, plain values)
  3. Reproduce with a minimal `serde_json::to_string(&cache)` call and inspect the serde error message

Example fix

// before
let json = serde_json::to_string_pretty(&cache).expect("serialize");
// after
let json = serde_json::to_string_pretty(&cache)
    .unwrap_or_else(|e| panic!("cache serialize failed: {e}"));
Defensive patterns

Strategy: try-catch

Validate before calling

// Serialize-ability can be checked cheaply in debug builds
debug_assert!(serde_json::to_string(&cache).is_ok(), "CatalogCache must remain JSON-serializable");

Try / catch

let json = serde_json::to_string_pretty(&cache)
    .unwrap_or_else(|e| panic!("CatalogCache serialization failed: {e}"));

Prevention

When it happens

Trigger: Adding a field to `CatalogCache` or `ModelEntry` whose `Serialize` impl returns Err (e.g. a map with non-string keys, or a manual impl writing via a failing formatter); swapping serde_json for a serializer that rejects the type shape.

Common situations: Introducing custom serialization for credentials or cost fields; using `serde_json::to_writer` with types that changed from struct to map-of-enums.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/77b1870ab79f98f2. Report an issue: GitHub.

Appendix: source

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

            MergedCatalog::from_sources(BTreeMap::new(), Some(provider_cache), bundled, now);
        let resolved = merged.resolve("sample/model").expect("resolved");
        assert_eq!(resolved.context_window, Some(1_000));
        assert_eq!(resolved.provenance, MetadataProvenance::Bundled);
    }

    #[test]
    fn cache_roundtrip_serializes_no_secret_fields() {
        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)