Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,9 +1181,10 @@ impl Agent {
let mut messages_to_add = Conversation::default();
let mut tools_updated = false;
let mut did_recovery_compact_this_iteration = false;
let mut exit_chat = false;

while let Some(next) = stream.next().await {
if is_token_cancelled(&cancel_token) {
if is_token_cancelled(&cancel_token) || exit_chat {
break;
Comment on lines +1187 to 1188
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check exit state before consuming next stream chunk

After exit_chat is set by a malformed tool call, the loop still awaits stream.next() in the while let condition and only then breaks at this guard, so that fetched chunk is discarded without processing. In OpenAI streaming, usage can arrive as a separate usage-only event (response_to_streaming_message, crates/goose/src/providers/formats/openai.rs, choices.is_empty() branch), which means this path can skip update_session_metrics and undercount token/cost metrics on parse-failure exits.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

that seems unlikely since we ran out of tokens and the provider just bailed

}

Expand Down Expand Up @@ -1462,15 +1463,17 @@ impl Agent {
yield AgentEvent::Message(final_response.clone());
messages_to_add.push(final_response);
} else {
let error_msg = format!(
"[system: Tool call could not be parsed: {}. The response may have been truncated. Try breaking the task into smaller steps.]",
error!(
"Tool call could not be parsed: {}",
request.tool_call.as_ref().unwrap_err(),
);
let error_response = Message::user()
.with_generated_id()
.with_text(&error_msg);
yield AgentEvent::Message(error_response.clone());
messages_to_add.push(error_response);
yield AgentEvent::Message(
Message::assistant().with_text(
"A tool call could not be parsed — the response may have been truncated. Try breaking the task into smaller steps or resending your message."
)
);
exit_chat = true;
break;
Comment on lines +1475 to +1476
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not skip later tool outputs after one parse failure

Breaking here exits the per-request loop on the first malformed tool JSON, but tool handling for the whole request set has already run earlier in this iteration. If one tool call is malformed and a later one is valid, the later result is never yielded or added to messages_to_add, so users can miss outputs (or side effects) from tools that already executed and the persisted conversation becomes incomplete.

Useful? React with 👍 / 👎.

}
}

Expand Down Expand Up @@ -1590,7 +1593,6 @@ impl Agent {
}
}

let mut exit_chat = false;
if no_tools_called {
if let Some(final_output_tool) = self.final_output_tool.lock().await.as_ref() {
if final_output_tool.final_output.is_none() {
Expand Down
Loading