Skip to content
Closed
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,19 @@ public static ToolResultBlock text(String text) {
/**
* Create an error result (for tool method return values).
*
* <p>The result carries {@link ToolResultState#ERROR} so that failures are
* recognized by state, independent of the output text prefix.
*
* @param errorMessage Error message
* @return ToolResultBlock with error output
* @return ToolResultBlock with error output and {@code state = ERROR}
*/
public static ToolResultBlock error(String errorMessage) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[praise] Clean fix — setting the structured state field directly in the factory method is the right approach. It fixes all error call sites (MCP timeouts, tool-not-found, generic failures) in one place and removes the dependency on the brittle "[ERROR]" text-prefix heuristic in determineToolResultState().

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[praise] Clean fix — setting the structured state field directly in the factory method is the right approach. It fixes all error call sites (MCP timeouts, tool-not-found, generic failures) in one place and removes the dependency on the brittle "[ERROR]" text-prefix heuristic in determineToolResultState().

return new ToolResultBlock(
null,
null,
List.of(TextBlock.builder().text("Error: " + errorMessage).build()),
null);
null,
ToolResultState.ERROR);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.agentscope.core.message.ContentBlock;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.message.ToolResultBlock;
import io.agentscope.core.message.ToolResultState;
import io.agentscope.core.message.ToolUseBlock;
import io.agentscope.core.tool.test.SampleTools;
import io.agentscope.core.tool.test.ToolTestUtils;
Expand Down Expand Up @@ -124,6 +125,20 @@ void shouldReturnErrorWhenToolThrows() {
"Error: Tool execution failed: Tool error: test failure",
content,
"Error message should be wrapped by executor");
assertEquals(
ToolResultState.ERROR,
responses.get(0).getState(),
"A failed tool call must be reported with state=ERROR, not SUCCESS");
}

@Test
@DisplayName("ToolResultBlock.error() carries state=ERROR (issue #2157)")
void errorResultCarriesErrorState() {
// Regression guard: previously error() left state=null (defaulting to
// RUNNING) and relied on an "[ERROR]" text prefix it never produced, so
// determineToolResultState() misclassified failures as SUCCESS.
ToolResultBlock result = ToolResultBlock.error("boom");
assertEquals(ToolResultState.ERROR, result.getState());
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[praise] Excellent regression guard with a clear historical comment explaining why the bug existed (state defaulted to RUNNING, text prefix was "Error: " not "[ERROR]"). This level of context in a test comment is exactly what future maintainers need to understand the intent without digging through git history.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[praise] Excellent regression guard with a clear historical comment explaining why the bug existed (state defaulted to RUNNING, text prefix was "Error: " not "[ERROR]"). This level of context in a test comment is exactly what future maintainers need to understand the intent without digging through git history.


@Test
Expand Down
Loading