Hmbown/CodeWhale · error
serialize runtime event envelope
Error message
serialize runtime event envelope
What it means
This `expect` panics when `serde_json::to_value(envelope)` fails to serialize a `RuntimeEventRecord` into a JSON `Value`. The library authors treat runtime event envelopes as trivially serializable, so any serialization failure is considered a programmer bug rather than a recoverable runtime condition. It is thrown in `runtime_api.rs` while building the JSON payload for a runtime event.
Solutions
- Inspect the failing payload variant: run with a backtrace (RUST_BACKTRACE=1) and identify which field of `RuntimeEventRecord` fails to serialize.
- Ensure all fields use JSON-compatible types (map keys must be strings or string-like; avoid `serde_json::Value` objects with non-string keys).
- Replace the `expect` with a `Result` return and propagate the serialization error to the caller.
- Add a serialization round-trip test for the new payload variant.
Example fix
// before
serde_json::to_value(envelope).expect("serialize runtime event envelope")
// after
serde_json::to_value(&envelope)
.map_err(|e| ToolError::execution_failed(format!("serialize runtime event envelope: {e}")))? Defensive patterns
Strategy: try-catch
Validate before calling
// Caller-side: ensure the payload round-trips before handing it to the runtime
fn ensure_json_serializable<T: serde::Serialize>(v: &T) -> Result<(), serde_json::Error> {
serde_json::to_value(v).map(|_| ())
} Type guard
fn is_json_safe(v: &serde_json::Value) -> bool {
match v {
serde_json::Value::Object(m) => m.iter().all(|(k, x)| is_json_safe(x)),
serde_json::Value::Array(a) => a.iter().all(is_json_safe),
_ => true,
}
} Try / catch
// Rust: replace expect with explicit error propagation
let value = serde_json::to_value(&envelope)
.map_err(|e| ToolError::execution_failed(format!("envelope serialization failed: {e}")))?; Prevention
- Keep envelope fields JSON-compatible (string map keys, plain types).
- Add a round-trip serialization test for every new RuntimeEventRecord payload variant.
- Prefer Result-returning serialization at API boundaries over expect/unwrap.
- Run with RUST_BACKTRACE=1 in CI so panics locate the offending field quickly.
When it happens
Trigger: Calling the runtime event envelope builder in crates/tui/src/runtime_api.rs:5898 when `event.payload` (or another envelope field like `timestamp`/`created_at`) contains a type whose `Serialize` implementation fails — e.g. a non-string map key unsupported by serde_json or a serializer error from a custom Serialize impl on the payload.
Common situations: Adding a new field or payload variant to `RuntimeEventRecord` whose serde_json serialization fails (e.g. heterogeneous map keys, unserializable nested types); swapping the payload type to a custom serde type that errors at runtime.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- <dynamic: wrapped serde_json serialization error>
- <dynamic: wrapped serde_json serialization error>
- finite points
- <serde_json serialization error>
- (serde_json serialization error wrapped as…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/71814105198550e5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_api.rs:5898
fn runtime_event_payload(event: crate::runtime_threads::RuntimeEventRecord) -> serde_json::Value {
let event_name = event.event.clone();
let timestamp = event.timestamp.to_rfc3339();
let schema_version = RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION;
let envelope = RuntimeEventEnvelope {
schema_version,
seq: event.seq,
event: event_name.clone(),
kind: event_name,
thread_id: event.thread_id,
turn_id: event.turn_id,
item_id: event.item_id,
timestamp: timestamp.clone(),
created_at: Some(timestamp),
payload: event.payload,
extra: Default::default(),
};
serde_json::to_value(envelope).expect("serialize runtime event envelope")
}
fn runtime_event_payload_with_previous(
event: crate::runtime_threads::RuntimeEventRecord,
previous_seq: u64,
) -> serde_json::Value {
let mut payload = runtime_event_payload(event);
if let Some(object) = payload.as_object_mut() {
object.insert("previous_seq".to_string(), json!(previous_seq));
}
payload
}
fn map_compat_stream_event(event: &crate::runtime_threads::RuntimeEventRecord) -> Option<SseEvent> {
let payload = &event.payload;
match event.event.as_str() {
"item.delta" => {
let kind = payloadView on GitHub (pinned to 433685b202)