Hmbown/CodeWhale · error
ApplyPatchPreflight should serialize
Error message
ApplyPatchPreflight should serialize
What it means
Panic building audit metadata for `apply_patch`: the `ApplyPatchPreflight` struct (derived `Serialize`) is converted with `serde_json::to_value` to emit the `apply_patch.preflight` event. For a derived struct over plain data this conversion is effectively infallible; it fails only if a field is not JSON-representable — non-string map keys or a non-finite float in a newly added field.
Source
Thrown at crates/tui/src/tools/apply_patch.rs:697
Ok(ApplyPatchPreflight {
files_total: changes.len(),
touched_files,
hunks_total: 0,
creates: Vec::new(),
deletes: Vec::new(),
path_override: None,
header_path_mismatch: None,
})
}
fn apply_patch_result_metadata(
preflight: &ApplyPatchPreflight,
pending: &[PendingWrite],
stats: &PatchStatsExt,
) -> Value {
let mut metadata =
serde_json::to_value(preflight).expect("ApplyPatchPreflight should serialize");
if let Some(object) = metadata.as_object_mut() {
object.insert("event".to_string(), json!("apply_patch.preflight"));
object.insert(
"mutation".to_string(),
build_mutation_metadata(pending, &stats.file_summaries),
);
}
metadata
}
/// Preserve the exact applied before/after diff independently from approval
/// presentation. The TUI consumes this success-only metadata for its calm
/// File receipt; the normal model-facing result remains compact JSON.
fn build_mutation_metadata(pending: &[PendingWrite], summaries: &[FileSummary]) -> Value {
let mut matched = HashSet::new();
let mut renames = Vec::new();
for (delete_index, (deleted, delete_summary)) in pending.iter().zip(summaries).enumerate() {View on GitHub (pinned to 0c42157ee5)
Solutions
- Change the offending field to string-keyed maps (`BTreeMap<String, _>`) or sanitize floats, then rebuild.
- Convert the expect to `.context(...)?` so the failure propagates into the patch tool's error path instead of crashing the turn.
- Add a unit test that round-trips a populated preflight through `serde_json::to_value`.
- Keep the struct JSON-shaped by construction: only String/number/bool/Vec/Option fields and string-keyed maps.
Example fix
// before
serde_json::to_value(preflight).expect("ApplyPatchPreflight should serialize")
// after: propagate into the tool's error path
let mut metadata = serde_json::to_value(preflight)
.context("serialize ApplyPatchPreflight for audit metadata")?; Defensive patterns
Strategy: validation
Validate before calling
// Prove the preflight serializes before emitting the audit event
if serde_json::to_value(&preflight).is_err() {
return emit_without_preflight_metadata();
} Type guard
fn json_shaped(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_shaped),
serde_json::Value::Object(o) => o.values().all(json_shaped),
_ => true,
}
} Prevention
- Keep ApplyPatchPreflight fields JSON-shaped: string keys, finite numbers.
- Round-trip test the struct through `serde_json::to_value` whenever fields are added.
When it happens
Trigger: Adding a field to `ApplyPatchPreflight` typed as a non-string-keyed map (`HashMap<u32, _>`, `HashMap<PathBuf, _>`) or carrying `f64::NAN`; the first apply_patch call after that build panics while emitting preflight metadata.
Common situations: Schema growth on the preflight struct during patch-tool development; copying types from a binary/TOML config context into the JSON metadata context without adjusting key types.
Related errors
- runtime event JSON is serializable
- validated Fleet tool authority envelope must serialize
- bundled model catalog must parse
- validated sandbox permission
- sandbox escalation was validated while planning
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/b22e6205b7f89366.
Report an issue: GitHub.