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

  1. Inspect the failing payload variant: run with a backtrace (RUST_BACKTRACE=1) and identify which field of `RuntimeEventRecord` fails to serialize.
  2. Ensure all fields use JSON-compatible types (map keys must be strings or string-like; avoid `serde_json::Value` objects with non-string keys).
  3. Replace the `expect` with a `Result` return and propagate the serialization error to the caller.
  4. 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

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


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 = payload

View on GitHub (pinned to 433685b202)