Log malformed tool argument JSON instead of silently falling back to string

Parsing tc.arguments with unwrap_or_else silently converts invalid JSON to a string value, which can mask real parsing errors. Add logging at three sites where this pattern occurs: - pretty_tool_arguments (chat.rs): logs both JSON parse errors and pretty-print failures - ToolCall event creation (main.rs): logs JSON parse errors when sending tool call updates to ACP clients - Permission request creation (main.rs): logs JSON parse errors when requesting permission for a tool call This allows developers to identify malformed argument JSON in logs, while maintaining backward compatibility (still falling back to string repr on error). Co-Authored-By: siGit Code <noreply@sigit.si>

paydii committed Aug 30, 2026 at 13:58 UTC 4430d520e364ac7b7ae56ad7d17e5621e2ed94b6
2 files changed +19 -8
src/chat.rs
+11 -5
@@ -186,13 +186,19 @@ pub(crate) fn tool_title(name: &str, arguments_json: &str) -> String {
186 }
187
188 /// Pretty-print a tool call's JSON arguments for the expanded view; malformed
189 -/// JSON is shown raw.
189 +/// JSON is logged and shown raw.
190 #[cfg_attr(not(unix), allow(dead_code))]
191 pub(crate) fn pretty_tool_arguments(arguments_json: &str) -> String {
192 - serde_json::from_str::<serde_json::Value>(arguments_json)
193 - .ok()
194 - .and_then(|value| serde_json::to_string_pretty(&value).ok())
195 - .unwrap_or_else(|| arguments_json.to_string())
192 + match serde_json::from_str::<serde_json::Value>(arguments_json) {
193 + Ok(value) => serde_json::to_string_pretty(&value).unwrap_or_else(|e| {
194 + log::warn!("failed to pretty-print tool arguments: {e}");
195 + arguments_json.to_string()
196 + }),
197 + Err(e) => {
198 + log::warn!("malformed JSON in tool arguments: {e}");
199 + arguments_json.to_string()
200 + }
201 + }
202 }
203
204 /// The tail of a tool's output for display under an expanded tool entry,
src/main.rs
+8 -3
@@ -1523,7 +1523,10 @@ impl SiGitAgent {
1523 // leaving an otherwise working tool round visually indistinct
1524 // from a stalled response.
1525 let raw_input: serde_json::Value = serde_json::from_str(&tc.arguments)
1526 - .unwrap_or_else(|_| serde_json::Value::String(tc.arguments.clone()));
1526 + .unwrap_or_else(|e| {
1527 + log::warn!("malformed JSON in tool arguments for '{}': {e}", tc.name);
1528 + serde_json::Value::String(tc.arguments.clone())
1529 + });
1530 self.send_tool_call_update(
1531 cx,
1532 session_id.clone(),
@@ -1734,8 +1737,10 @@ impl SiGitAgent {
1737 } else {
1738 format!("{tool_name}({args_preview})")
1739 };
1737 - let raw_input: serde_json::Value = serde_json::from_str(arguments)
1738 - .unwrap_or_else(|_| serde_json::Value::String(arguments.to_string()));
1740 + let raw_input: serde_json::Value = serde_json::from_str(arguments).unwrap_or_else(|e| {
1741 + log::warn!("malformed JSON in tool arguments for '{}': {e}", tool_name);
1742 + serde_json::Value::String(arguments.to_string())
1743 + });
1744
1745 let request = RequestPermissionRequest::new(
1746 session_id.clone(),