Skip to content

fix(ai): tool error handling#2671

Merged
ehayes2000 merged 1 commit into
mainfrom
fix-tool-errors
Apr 20, 2026
Merged

fix(ai): tool error handling#2671
ehayes2000 merged 1 commit into
mainfrom
fix-tool-errors

Conversation

@ehayes2000
Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@ehayes2000 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 41 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 43 minutes and 41 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a34d45b1-0038-4216-a3b9-d2ab94141ba7

📥 Commits

Reviewing files that changed from the base of the PR and between 0de6c43 and add86ac.

📒 Files selected for processing (3)
  • rust/cloud-storage/ai/src/tool/tool_loop/chat/agent.rs
  • rust/cloud-storage/ai/src/tool/tool_loop/chat/test.rs
  • rust/cloud-storage/ai/src/types/providers/openai/message.rs
📝 Walkthrough

Walkthrough

Renamed local variables in the chat tool-loop, changed how tool call responses are turned into text, added tracing error logs for tool failures, simplified JSON-vs-error classification for tool responses in OpenAI message conversion, and updated a test fixture and debug output.

Changes

Cohort / File(s) Summary
Agent Tool Processing
rust/cloud-storage/ai/src/tool/tool_loop/chat/agent.rs
Renamed accumulators (messagesnew_messages, tool_stream_partstool_responses), changed tool_responsetool_response_text to use formatted JSON or error description, and added tracing::error! logs for tool execution and call failures. Returned ProcessedStream fields updated accordingly.
OpenAI Message Conversion
rust/cloud-storage/ai/src/types/providers/openai/message.rs
Removed special structured-error detection; convert_message now attempts serde_json::from_str and on success emits ToolCallResponseJson, otherwise emits ToolCallErr with the raw response text. ToolCallErr output now uses the raw description string instead of a JSON-wrapped error object.
Tests
rust/cloud-storage/ai/src/tool/tool_loop/chat/test.rs
Test fixture for tool error now uses plain text "Tool execution failed" as tool message content and added eprintln! debug output before panic on mismatch.

Possibly related PRs

  • fix: anthropic no response #2189: Edits the same chat tool-loop implementation (process_stream_parts), indicating closely related changes to tool-loop behavior and structure.
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, but the changeset is focused on tool error handling improvements across three related files in the AI module. Consider adding a description explaining the motivation for the error handling changes and how they improve the system's behavior or reliability.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with 'fix:' prefix, is under 72 characters, and clearly describes the main change: improving tool error handling in the AI module.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rust/cloud-storage/ai/src/types/providers/openai/message.rs`:
- Around line 381-392: The serializer for AssistantMessagePart::ToolCallErr is
emitting a JSON object (e.g., {"type":"error","description":...}) while the
deserializer (in the match building assistant_part) treats any parseable JSON as
ToolCallResponseJson, breaking roundtrips; update the outgoing serialization
code that constructs the tool message (the branch that emits ToolCallErr,
referenced by AssistantMessagePart::ToolCallErr and the tool_response_text
logic) to write plain description text (the same plain-text fail.description /
"Error calling tool: ..." string used in agent.rs) instead of wrapping it in a
JSON object so the incoming serde_json::from_str will fail and land in the Err
arm; also remove the now-unused ToolResponseParseError struct if present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3a1bf1bd-57ab-4773-b20b-f2b8f5bdc66a

📥 Commits

Reviewing files that changed from the base of the PR and between 604ac32 and 88a7cc3.

📒 Files selected for processing (2)
  • rust/cloud-storage/ai/src/tool/tool_loop/chat/agent.rs
  • rust/cloud-storage/ai/src/types/providers/openai/message.rs

Comment thread rust/cloud-storage/ai/src/types/providers/openai/message.rs
@ehayes2000 ehayes2000 force-pushed the fix-tool-errors branch 2 times, most recently from 05be68a to 0de6c43 Compare April 20, 2026 20:10
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
rust/cloud-storage/ai/src/types/providers/openai/message.rs (1)

367-377: ⚠️ Potential issue | 🟠 Major

Avoid using JSON parseability as the only error/success discriminator.

An error description that is itself valid JSON will roundtrip as ToolCallResponseJson instead of ToolCallErr because serde_json::from_str succeeds. For example, a tool failure description like {"error":"permission denied"} or null loses its error semantics here. Consider preserving an explicit error marker/type channel or adding a non-JSON error envelope/prefix before relying on this parser. This is related to the prior roundtrip concern but remains possible for valid-JSON descriptions.

Suggested regression test
 #[test]
 fn test_assistant_with_tool_error_roundtrip() {
@@
 }
+
+#[test]
+fn test_assistant_with_json_like_tool_error_roundtrip() {
+    let tool_call_id = "call_123".to_string();
+    let tool_name = "get_weather".to_string();
+    let original = ChatMessage {
+        role: Role::Assistant,
+        content: ChatMessageContent::AssistantMessageParts(vec![
+            AssistantMessagePart::ToolCallErr {
+                name: tool_name.clone(),
+                description: r#"{"error":"permission denied"}"#.to_string(),
+                id: tool_call_id.clone(),
+            },
+        ]),
+        image_urls: None,
+    };
+
+    let openai_msgs: Vec<ChatCompletionRequestMessage> = original.clone().into();
+    let mut tool_mapping = HashMap::new();
+    tool_mapping.insert(tool_call_id, tool_name);
+
+    let converted_back =
+        convert_message(openai_msgs.into_iter().next().unwrap(), Some(&tool_mapping));
+
+    assert_eq!(original, converted_back);
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/cloud-storage/ai/src/types/providers/openai/message.rs` around lines 367
- 377, The code currently treats any valid JSON response_text as success by
using serde_json::from_str in the AssistantMessagePart creation, which
misclassifies JSON-formatted error descriptions; change the decision logic in
the block that builds AssistantMessagePart (around response_text, tool_msg, and
AssistantMessagePart::ToolCallResponseJson/ToolCallErr) to first consult an
explicit error indicator from the tool call (e.g., a flag on tool_msg like
is_error/ok or a dedicated error field) or require a non-JSON error
envelope/prefix (e.g., "ERROR:"/{"__error":...}) before parsing JSON; only call
serde_json::from_str when the explicit indicator says success (or after
stripping/validating a non-JSON envelope), otherwise construct ToolCallErr with
the raw response_text and id so genuine error semantics are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rust/cloud-storage/ai/src/types/providers/openai/message.rs`:
- Around line 589-590: The test contains an unconditional debug print
eprintln!("{:#?}", openai_msgs); which pollutes test output; remove that line
(or replace it with a conditional/log macro if you truly need debug only under a
debug flag) so the test uses only the existing assertion; locate the creation
flow where openai_msgs is set (the let openai_msgs:
Vec<ChatCompletionRequestMessage> = original.clone().into(); line in message.rs)
and delete the eprintln! call.

---

Duplicate comments:
In `@rust/cloud-storage/ai/src/types/providers/openai/message.rs`:
- Around line 367-377: The code currently treats any valid JSON response_text as
success by using serde_json::from_str in the AssistantMessagePart creation,
which misclassifies JSON-formatted error descriptions; change the decision logic
in the block that builds AssistantMessagePart (around response_text, tool_msg,
and AssistantMessagePart::ToolCallResponseJson/ToolCallErr) to first consult an
explicit error indicator from the tool call (e.g., a flag on tool_msg like
is_error/ok or a dedicated error field) or require a non-JSON error
envelope/prefix (e.g., "ERROR:"/{"__error":...}) before parsing JSON; only call
serde_json::from_str when the explicit indicator says success (or after
stripping/validating a non-JSON envelope), otherwise construct ToolCallErr with
the raw response_text and id so genuine error semantics are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3d689c84-f331-44be-bb81-bf6d975a38ce

📥 Commits

Reviewing files that changed from the base of the PR and between 88a7cc3 and 0de6c43.

📒 Files selected for processing (3)
  • rust/cloud-storage/ai/src/tool/tool_loop/chat/agent.rs
  • rust/cloud-storage/ai/src/tool/tool_loop/chat/test.rs
  • rust/cloud-storage/ai/src/types/providers/openai/message.rs

Comment on lines +589 to +590
let openai_msgs: Vec<ChatCompletionRequestMessage> = original.clone().into();
eprintln!("{:#?}", openai_msgs);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove the unconditional debug print from the test.

eprintln! will add noise under --nocapture or when the test fails; the assertion already prints the diff context.

Clean up debug output
         let openai_msgs: Vec<ChatCompletionRequestMessage> = original.clone().into();
-        eprintln!("{:#?}", openai_msgs);
 
         // Create tool call mapping for conversion back
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let openai_msgs: Vec<ChatCompletionRequestMessage> = original.clone().into();
eprintln!("{:#?}", openai_msgs);
let openai_msgs: Vec<ChatCompletionRequestMessage> = original.clone().into();
// Create tool call mapping for conversion back
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/cloud-storage/ai/src/types/providers/openai/message.rs` around lines 589
- 590, The test contains an unconditional debug print eprintln!("{:#?}",
openai_msgs); which pollutes test output; remove that line (or replace it with a
conditional/log macro if you truly need debug only under a debug flag) so the
test uses only the existing assertion; locate the creation flow where
openai_msgs is set (the let openai_msgs: Vec<ChatCompletionRequestMessage> =
original.clone().into(); line in message.rs) and delete the eprintln! call.

@ehayes2000 ehayes2000 merged commit 80ea6e6 into main Apr 20, 2026
39 checks passed
@ehayes2000 ehayes2000 deleted the fix-tool-errors branch April 20, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant