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
- 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`.
- Change `expected_checksum` to return `Result<String, serde_json::Error>` and let `record`/`validate` surface the error instead of panicking.
- Pin the canonical JSON shape with a golden checksum test so schema edits are caught in CI.
- 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
- Sanitize floats (`.filter(|f| f.is_finite())`) before building ledger payloads.
- Keep a golden checksum test so changes to the canonical JSON shape are caught in CI.
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
- validated Fleet tool authority envelope must serialize
- bundled model catalog must parse
- ApplyPatchPreflight should serialize
- validated sandbox permission
- sandbox escalation was validated while planning
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/5ebb86757569059b.
Report an issue: GitHub.