Skip to content

fix(adk/bedrock): coerce nil tool-call args to empty object#2222

Open
go-faustino wants to merge 1 commit into
kagent-dev:mainfrom
go-faustino:fix/bedrock-nil-tool-input
Open

fix(adk/bedrock): coerce nil tool-call args to empty object#2222
go-faustino wants to merge 1 commit into
kagent-dev:mainfrom
go-faustino:fix/bedrock-nil-tool-input

Conversation

@go-faustino

Copy link
Copy Markdown
Contributor

Summary

The Bedrock converter serializes a no-argument tool call as toolUse.input: null, which Bedrock Converse rejects on the follow-up (tool-result) turn with:

ValidationException: Malformed input request
(The value at messages.N.content.M.toolUse.input is empty)

In convertGenaiContentsToBedrockMessages (go/adk/pkg/models/bedrock.go), a tool call's input is built as document.NewLazyDocument(part.FunctionCall.Args). A no-argument call reaches this point with a nil Args map, because genai's FunctionCall.Args is tagged json:"args,omitempty": an empty map[string]any{} is dropped when the event is persisted to the session store and reloaded as nil. document.NewLazyDocument(nil) then serializes to JSON null, and Bedrock requires toolUse.input to be a JSON object ({}).

This is reproducible against us.anthropic.claude-opus-4-8 in any multi-turn conversation where the model calls a tool that takes no arguments (e.g. datetime_get_current_time) and the history is reloaded before the next turn.

Fix

Coerce a nil Args map to an empty map[string]any{} before building the ToolUseBlock, so no-argument tool calls round-trip as {} instead of null.

Test

Adds TestConvertGenaiContentsNoArgToolInput covering both nil and empty ({}) Args, asserting the produced toolUse.input is a non-null, empty JSON object. Without the fix, the nil case serializes to null and the test fails.

go test ./pkg/models/ -run TestConvertGenaiContents
ok  github.com/kagent-dev/kagent/go/adk/pkg/models

Copilot AI review requested due to automatic review settings July 13, 2026 09:17
@github-actions github-actions Bot added the bug Something isn't working label Jul 13, 2026

Copilot AI left a comment

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.

Pull request overview

Fixes Bedrock Converse tool-call serialization by ensuring no-argument tool calls produce toolUse.input as an empty JSON object ({}) instead of null, which Bedrock rejects on follow-up turns.

Changes:

  • Coerces nil FunctionCall.Args to an empty map[string]any{} before creating the Bedrock ToolUseBlock.
  • Adds a unit test to cover both nil and empty-map tool arguments, asserting toolUse.input is a non-null empty object.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
go/adk/pkg/models/bedrock.go Coerces nil tool-call args to an empty object before building toolUse.input.
go/adk/pkg/models/bedrock_test.go Adds coverage for no-arg tool calls to ensure toolUse.input is {} not null.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// rejects a null input with "ValidationException: Malformed input request"
// ("The value at messages.N.content.M.toolUse.input is empty").
func TestConvertGenaiContentsNoArgToolInput(t *testing.T) {
for _, args := range []map[string]any{nil, {}} {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review. This one is a false positive — the code compiles and the tests pass as-is.

In a Go composite literal, the element type may be elided, so {} inside []map[string]any{nil, {}} is shorthand for map[string]any{} (Go spec, Composite literals: "Within a composite literal of array, slice, or map type T, elements or map keys that are themselves composite literals may elide the respective literal type if it is identical to the element or key type of T"). nil is also a valid map[string]any element.

Verified on the branch:

$ go vet ./pkg/models/
$ go test ./pkg/models/ -run TestConvertGenaiContentsNoArgToolInput -count=1
ok  github.com/kagent-dev/kagent/go/adk/pkg/models  0.855s

Keeping the elided form as it's idiomatic, but happy to spell out map[string]any{} if maintainers prefer explicitness.

A tool call with no arguments reaches the Bedrock converter with a nil
FunctionCall.Args map: genai's FunctionCall.Args is `json:"args,omitempty"`,
so an empty map is dropped when the event is persisted to the session store
and reloaded as nil. document.NewLazyDocument(nil) then serializes
toolUse.input to `null`, and Bedrock Converse rejects the follow-up
(tool-result) turn with:

  ValidationException: Malformed input request
  (The value at messages.N.content.M.toolUse.input is empty)

Coerce nil args to an empty map[string]any{} so no-argument tool calls
round-trip as `{}`. Adds a unit test covering both nil and empty Args.

Signed-off-by: Gonçalo Faustino <goncalo.santos@wellhub.com>
@go-faustino go-faustino force-pushed the fix/bedrock-nil-tool-input branch from d4594c0 to b6ec5e5 Compare July 13, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants