Skip to content

fix(adk): flag the function response of a failed tool call with isError - #2937

Open
fiunchinho wants to merge 3 commits into
kagent-dev:mainfrom
giantswarm:upstream/adk-tool-error-flag
Open

fiunchinho wants to merge 3 commits into
kagent-dev:mainfrom
giantswarm:upstream/adk-tool-error-flag

Conversation

@fiunchinho

@fiunchinho fiunchinho commented Sep 24, 2026 •

Copy link
Copy Markdown

Problem

The runtimes do not agree on how a function_response says that a tool call failed. The harness runtimes set isError beside a failed result (go/harness/runtime/a2a/executor.go, toolResultPart), and the Python ADK passes the MCP isError through. The Go ADK sends only {"error": "<text>"}: adk-go's mcptoolset turns an MCP result with isError into a Go error, and the flow renders every tool error as {"error": text} (internal/llminternal/base_flow.go, callTool).

So an A2A client needs two checks to tell a failed call from a successful one: isError for the harness and Python runtimes, the error key for the Go ADK. The kagent UI does both (ToolCallCard.tsx), so it is not affected. A client that reads only isError shows a failed Go ADK call as a successful step. Seen with such a client: a Prometheus query the server rejected showed a green check, while the model's text quoted the parse error.

Change

makeOnToolErrorCallback returns {"error": text, "isError": true} instead of leaving the response to adk-go's default {"error": text}. The model reads the same text as before; the function_response part carries the same flag on every runtime.

  • A confirmation request (tool.ErrConfirmationRequired) travels as an error too, but it is a pause, not a failure: it keeps the plain shape, which clients tell apart by its text.
  • A rejected call (tool.ErrConfirmationRejected) did not run and is flagged.
  • adk-go treats a response from the callback as the error handled, so the after-tool callback now logs such a call as tool execution completed with result_keys=[error isError]. The error-level tool execution failed line from the on-error callback is unchanged.

Tests

cd go && go test -race ./adk/pkg/agent/...: the tests run an agent through adk-go's runner with the tool callbacks createGoogleADKAgent wires, and read the function responses the flow emits. A failed call is flagged; a confirmation request keeps the plain shape and is followed by the confirmation request; the rejection of that request does not run the tool and is flagged. go test -race ./adk/..., gofmt and go vet are clean.

adk-go's MCP toolset turns a CallToolResult that carries isError into a
Go error, and the flow renders every tool error as {"error": text}. The
harness runtimes (harness/runtime/a2a) set isError beside a failed
result, so a client that shows a step as failed on that flag sees a
failed Go ADK tool as a successful step, with the error prose as its
output.

The on-tool-error callback now returns the same {"error": text} with
"isError": true. The model reads the text it read before; the A2A
function_response part carries the flag, as it does for the other
runtimes. A confirmation request is a pause, not a failure, and keeps
the plain shape; a rejected call did not run and is flagged.

Signed-off-by: Jose Armesto <github@armesto.net>
Signed-off-by: Jose Armesto <github@armesto.net>
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale because of no activity in the last 15 days. It will be closed in the next 5 days unless it is tagged "no stalebot" or other activity occurs.

@github-actions github-actions Bot added the stale This issue or PR has become stale label Oct 15, 2026
Comment thread go/adk/pkg/agent/agent.go
if errors.Is(err, tool.ErrConfirmationRequired) {
return nil, nil
}
return map[string]any{"error": err.Error(), "isError": true}, nil

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.

our ui already flags error responses, which client?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

yeah, the kagent UI already handles this, this PR changes nothing there. What it does is give the Go ADK the same flag as the other two runtimes, so a client needs one check instead of two shapes (an error key for the Go ADK, isError for the rest). The error key alone is also a weak signal, since a tool that returns a map with an error key as a normal result renders as failed.

Comment thread go/adk/pkg/agent/tool_error_test.go Outdated
return &toolErrorContext{StrictContextMock: adkagent.NewStrictContextMock(context.Background())}
}

func TestOnToolErrorCallback_FlagsAFailedCall(t *testing.T) {

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.

tests only call the callback, never the real flow

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

agreed, I added better tests now, PTAL

The tests called the on-tool-error callback and read its return value.
They did not show that adk-go puts that value into the function
response, which is what a client reads.

They now run an agent through adk-go's runner with the tool callbacks
createGoogleADKAgent wires, and read the function responses the flow
emits: a failed call is flagged, a confirmation request keeps the plain
shape, and the rejection of that request does not run the tool and is
flagged.

Signed-off-by: Jose Armesto <github@armesto.net>
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Oct 19, 2026
Comment thread go/adk/pkg/agent/agent.go
if errors.Is(err, tool.ErrConfirmationRequired) {
return nil, nil
}
return map[string]any{"error": err.Error(), "isError": true}, nil

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.

mcp app tools lose this error text in compaction

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working stale This issue or PR has become stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants