Hmbown/CodeWhale · error · io::Error

<dynamic: wrapped serde_json serialization error>

Error message

<dynamic: wrapped serde_json serialization error>

What it means

publish_legacy_spillover_ownership serializes the LegacySpilloverOwnership struct (schema version, origin session, digest, size) with serde_json::to_vec_pretty before an atomic write. Serialization errors are wrapped in io::ErrorKind::InvalidData with the serde error attached. Given the simple field set, failures point to a type serde_json cannot represent, most commonly a non-string map key or invalid float introduced by a schema change.

Solutions

  1. Read the wrapped serde error to find the offending field in LegacySpilloverOwnership.
  2. Change non-string map keys to String/String-keyed maps and sanitize float fields (no NaN/Infinity).
  3. Use #[serde(skip_serializing_if = "Option::is_none")] or a custom Serialize for awkward types.
  4. Pin the struct with a round-trip unit test so future schema changes cannot silently break serialization.

Example fix

// before
struct LegacySpilloverOwnership { tags: HashMap<u32, String>, ... }
// after
struct LegacySpilloverOwnership { tags: BTreeMap<String, String>, ... }
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check types serde_json cannot represent when extending the struct
// reject non-string map keys and non-finite floats before constructing LegacySpilloverOwnership

Try / catch

// rust
let encoded = serde_json::to_vec_pretty(&ownership)
    .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; // log wrapped serde error to find the field

Prevention

When it happens

Trigger: apply_spillover_inner -> publish_legacy_spillover_ownership when serde_json::to_vec_pretty(&ownership) errors — e.g. after adding a field with a non-JSON-compatible type (HashMap<u64,_>, f64::NAN) or a failing custom Serialize impl.

Common situations: Recent schema evolution of LegacySpilloverOwnership added a problematic field; NaN/Infinity sizes or digests stored as floats; a custom serializer returning Err.

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@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/507b15dca68820cf. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/truncate.rs:153

    payload_path: &Path,
    session_id: &str,
    bytes: &[u8],
) -> io::Result<PathBuf> {
    if session_id.trim().is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "legacy spillover ownership requires a session id",
        ));
    }
    let ownership = LegacySpilloverOwnership {
        schema_version: LEGACY_SPILLOVER_OWNER_SCHEMA_VERSION,
        origin_session: session_id.to_string(),
        digest: crate::hashing::sha256_hex(bytes),
        size_bytes: bytes.len().try_into().unwrap_or(u64::MAX),
    };
    let sidecar = legacy_spillover_ownership_path(payload_path);
    let encoded = serde_json::to_vec_pretty(&ownership)
        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
    crate::utils::write_atomic(&sidecar, &encoded)?;
    Ok(sidecar)
}

pub(crate) fn read_legacy_spillover_ownership(
    payload_path: &Path,
) -> io::Result<LegacySpilloverOwnership> {
    let sidecar = legacy_spillover_ownership_path(payload_path);
    if std::fs::symlink_metadata(&sidecar)?
        .file_type()
        .is_symlink()
    {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "legacy spillover ownership sidecar must not be a symlink",
        ));
    }
    let ownership = serde_json::from_slice::<LegacySpilloverOwnership>(&std::fs::read(sidecar)?)

View on GitHub (pinned to 73e0f67d83)