BloopAI/vibe-kanban · error
Scratch serialization should not fail
Error message
Scratch serialization should not fail
What it means
This panic comes from `serde_json::to_value(scratch).expect(...)` inside `Patch::add` (crates/services/src/services/events/patches.rs:108). `serde_json::to_value` only fails when a type's Serialize impl errors — e.g. a custom serializer emitting a non-string map key or propagating an inner serialization error. The expectation is that `Scratch` is plain serializable data, so this indicates a broken Serialize implementation for `Scratch` or one of its payload variants.
Source
Thrown at crates/services/src/services/events/patches.rs:108
.expect("Workspace path should be valid"),
})])
}
}
/// Helper functions for creating scratch-specific patches.
/// All patches use path "/scratch" - filtering is done by matching id and payload type in the value.
pub mod scratch_patch {
use super::*;
const SCRATCH_PATH: &str = "/scratch";
/// Create patch for adding a new scratch
pub fn add(scratch: &Scratch) -> Patch {
Patch(vec![PatchOperation::Add(AddOperation {
path: SCRATCH_PATH
.try_into()
.expect("Scratch path should be valid"),
value: serde_json::to_value(scratch).expect("Scratch serialization should not fail"),
})])
}
/// Create patch for updating an existing scratch
pub fn replace(scratch: &Scratch) -> Patch {
Patch(vec![PatchOperation::Replace(ReplaceOperation {
path: SCRATCH_PATH
.try_into()
.expect("Scratch path should be valid"),
value: serde_json::to_value(scratch).expect("Scratch serialization should not fail"),
})])
}
/// Create patch for removing a scratch.
/// Uses Replace with deleted marker so clients can filter by id and payload type.
pub fn remove(scratch_id: Uuid, scratch_type_str: &str) -> Patch {
Patch(vec![PatchOperation::Replace(ReplaceOperation {
path: SCRATCH_PATHView on GitHub (pinned to 4deb7eca8f)
Solutions
- Inspect the Scratch value (especially `payload`) for data serde_json cannot represent (non-string map keys, NaN/Infinity floats); sanitize or remap such data before constructing the Scratch.
- Review the `Scratch`/payload Serialize impls (including custom ones) and fix any that can return Err so serialization is total.
- Temporarily replace `.expect` with `match`/`unwrap_err` logging to capture the real serde error message and target the failing field.
- If a third-party payload type is the culprit, wrap it with a lossy serializer (`serde_json::to_value(&val).unwrap_or(Value::Null)`-style) or convert it to plain serde_json::Value before embedding.
Example fix
// before
value: serde_json::to_value(scratch).expect("Scratch serialization should not fail"),
// after
value: serde_json::to_value(scratch).unwrap_or_else(|e| {
tracing::error!(error = ?e, "failed to serialize scratch");
serde_json::Value::Null
}), Defensive patterns
Strategy: type-guard
Validate before calling
// Pre-flight check before building the patch:
fn scratch_serializable(scratch: &Scratch) -> bool {
serde_json::to_value(scratch).is_ok()
} Type guard
fn is_json_safe(v: &serde_json::Value) -> bool {
use serde_json::Value::*;
match v {
Null | Bool(_) | Number(_) | String(_) => v.as_f64().map(|f| f.is_finite()).unwrap_or(true),
Array(a) => a.iter().all(is_json_safe),
Object(o) => o.values().all(is_json_safe),
}
} Try / catch
// .expect panics aren't catchable; prefer non-panicking construction:
match serde_json::to_value(scratch) {
Ok(value) => { /* build PatchOperation::Add */ }
Err(e) => tracing::error!(error = ?e, "scratch serialization failed"),
} Prevention
- Keep Scratch payloads plain serde_json-friendly data; avoid custom Serialize impls that can error.
- Ban NaN/Infinity floats and non-string map keys in payload data.
- Add a round-trip test (to_value then from_value) for every new Scratch payload variant.
- Capture the serde error message first when debugging to identify the offending field.
When it happens
Trigger: Calling `Patch::add(&scratch)` where serializing the `Scratch` (or its `payload`, including `scratch_type_str`-tagged payload types) returns `Err` — typically because a nested payload uses a serialization-impossible construct (non-string map keys, custom Serialize impls returning errors, f64 NaN/Infinity with restricted serde settings).
Common situations: Adding a new Scratch payload variant whose Serialize impl returns Err; using HashMap keys that aren't strings; NaN/Infinity floats in payload data; a downstream dependency changing serde behavior for a payload field.
Related errors
- Workspace serialization should not fail
- Default profiles v3 JSON is invalid
- Execution process serialization should not fail
- Scratch path should be valid
- request_id called for unsupported request variant
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/5816b595cf564ab3.
Report an issue: GitHub.