Hmbown/CodeWhale · error

runtime event JSON is serializable

Error message

runtime event JSON is serializable

What it means

Panic while computing the SHA-256 checksum of a runtime-contract ledger event. `expected_checksum` builds a `serde_json::Value` from the event's fields and serializes it with `serde_json::to_vec`; the expect exists to unwrap the `Result`. A `serde_json::Value` is essentially always serializable to JSON bytes, so this fires only when the canonical value contains something JSON cannot represent — in practice a non-finite float (`NaN`/`Infinity`) reaching `payload` through a custom `Serialize` impl on the way into the `json!` macro.

Source

Thrown at crates/tui/src/core/runtime_contract/ledger.rs:78

            checksum: String::new(),
        };
        event.checksum = event.expected_checksum();
        event
    }

    #[must_use]
    pub fn expected_checksum(&self) -> String {
        let canonical = serde_json::json!({
            "schema_version": self.schema_version,
            "sequence": self.sequence,
            "event_id": self.event_id,
            "kind": self.kind,
            "parent_event_id": self.parent_event_id,
            "causal_event_id": self.causal_event_id,
            "recorded_at_ms": self.recorded_at_ms,
            "payload": self.payload,
        });
        let bytes = serde_json::to_vec(&canonical).expect("runtime event JSON is serializable");
        let digest = Sha256::digest(bytes);
        digest.iter().map(|byte| format!("{byte:02x}")).collect()
    }

    pub fn validate(&self) -> Result<(), String> {
        if self.schema_version != RUNTIME_CONTRACT_SCHEMA_VERSION {
            return Err(format!(
                "unsupported runtime event schema {}",
                self.schema_version
            ));
        }
        if self.event_id.trim().is_empty() {
            return Err("runtime event ID cannot be empty".to_string());
        }
        let expected = self.expected_checksum();
        if self.checksum != expected {
            return Err(format!("runtime event {} checksum mismatch", self.event_id));
        }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Sanitize the payload before recording: replace non-finite floats with `null` or `0.0` (`f64::is_finite` check) and build the payload as a plain `serde_json::Value`.
  2. Change `expected_checksum` to return `Result<String, serde_json::Error>` and let `record`/`validate` surface the error instead of panicking.
  3. Pin the canonical JSON shape with a golden checksum test so schema edits are caught in CI.
  4. If you are chasing a checksum mismatch rather than the panic itself, compare `validate()` output field-by-field between writer and reader.

Example fix

// before
let bytes = serde_json::to_vec(&canonical).expect("runtime event JSON is serializable");

// after: surface the failure to the caller
let bytes = serde_json::to_vec(&canonical)
    .map_err(|err| format!("event {} is not JSON-serializable: {err}", self.event_id))?;
Defensive patterns

Strategy: validation

Validate before calling

// Prove the payload is JSON-representable before recording the event
if serde_json::to_vec(&event.payload).is_err() {
    return Err(format!("payload of event {} is not JSON-representable", event.event_id));
}

Type guard

fn json_representable(v: &serde_json::Value) -> bool {
    match v {
        serde_json::Value::Number(n) => n.as_f64().map_or(true, f64::is_finite),
        serde_json::Value::Array(a) => a.iter().all(json_representable),
        serde_json::Value::Object(o) => o.values().all(json_representable),
        _ => true,
    }
}

Prevention

When it happens

Trigger: A `RuntimeEvent` whose `payload` was built from a type with a hand-written `Serialize` that emits a non-finite f64 (division by zero or missing data producing NaN) or otherwise emits a value `to_vec` rejects; the first `expected_checksum()` call on that event panics.

Common situations: New ledger event kinds embedding computed metrics (latency ratios, rates) where a denominator can be zero; downstream tooling comparing checksums after the canonical JSON representation changed between versions.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/5ebb86757569059b. Report an issue: GitHub.