fix: prevent nil tool_use.input causing 400 Bad Request from Anthropic API

The assistant message stored in loop.go omitted the Arguments map from
ToolCall, leaving it nil. When sent back to Anthropic, nil serialized as
JSON null instead of {}, which the API rejects.

- Copy Arguments map into stored ToolCall in agent loop (root cause)
- Add nil-guard in Anthropic provider to recover from Function.Arguments
- Normalize ToolCalls when loading sessions from disk (migration)
This commit is contained in:
Rahul Bansal 2026-02-21 03:35:23 +05:30
parent c7a6016c2d
commit cfbb9fa9fe
3 changed files with 26 additions and 4 deletions

View file

@ -638,6 +638,7 @@ func (al *AgentLoop) runLLMIteration(
ID: tc.ID,
Type: "function",
Name: tc.Name,
Arguments: tc.Arguments,
Function: &providers.FunctionCall{
Name: tc.Name,
Arguments: string(argumentsJSON),

View file

@ -146,7 +146,20 @@ func buildParams(
blocks = append(blocks, anthropic.NewTextBlock(msg.Content))
}
for _, tc := range msg.ToolCalls {
blocks = append(blocks, anthropic.NewToolUseBlock(tc.ID, tc.Arguments, tc.Name))
input := tc.Arguments
if input == nil {
// Recover from nil Arguments (e.g. loaded from older sessions)
if tc.Function != nil && tc.Function.Arguments != "" {
var parsed map[string]interface{}
if err := json.Unmarshal([]byte(tc.Function.Arguments), &parsed); err == nil {
input = parsed
}
}
if input == nil {
input = map[string]interface{}{}
}
}
blocks = append(blocks, anthropic.NewToolUseBlock(tc.ID, input, tc.Name))
}
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(blocks...))
} else {

View file

@ -259,6 +259,14 @@ func (sm *SessionManager) loadSessions() error {
continue
}
// Normalize tool calls to ensure Arguments map is populated
// from Function.Arguments for sessions saved before this fix.
for i, msg := range session.Messages {
for j, tc := range msg.ToolCalls {
session.Messages[i].ToolCalls[j] = providers.NormalizeToolCall(tc)
}
}
sm.sessions[session.Key] = &session
}