Skip to content

fix: guard against empty tool_use/function.name in protocol conversion - #409

Open
spock-wen wants to merge 1 commit into
kittors:devfrom
spock-wen:fix/empty-tool-name-guard
Open

fix: guard against empty tool_use/function.name in protocol conversion#409
spock-wen wants to merge 1 commit into
kittors:devfrom
spock-wen:fix/empty-tool-name-guard

Conversation

@spock-wen

Copy link
Copy Markdown

Summary

Skip tool calls with empty function.name at all conversion points. Upstream providers occasionally return tool_calls with empty function.name, which produces downstream JSON with empty names that Claude Code rejects.

Changes

  • Streaming response: check name.String() != "" (not just Exists)
  • Non-streaming response: skip in convertOpenAINonStreamingToAnthropic
  • Non-streaming alternative: skip in ConvertOpenAIResponseToClaudeNonStream (both content-array and message-level tool_calls paths)
  • Request back-conversion: skip tool_use with empty name

Files

  • internal/translator/openai/claude/openai_claude_request.go (+5, -1)
  • internal/translator/openai/claude/openai_claude_response.go (+16, -4)

Test Plan

  • go test ./test/... passes locally

Skip tool calls with empty function.name at all conversion points:
- Streaming response: check name.String() != "" (not just Exists)
- Non-streaming response: skip in convertOpenAINonStreamingToAnthropic
- Non-streaming alternative: skip in ConvertOpenAIResponseToClaudeNonStream
  (both content-array and message-level tool_calls paths)
- Request back-conversion: skip tool_use with empty name

Upstream providers occasionally return tool_calls with empty function.name
which causes downstream JSON with empty names that Claude Code rejects.
@spock-wen spock-wen closed this Jun 11, 2026
@spock-wen
spock-wen deleted the fix/empty-tool-name-guard branch June 11, 2026 12:54
@spock-wen
spock-wen restored the fix/empty-tool-name-guard branch June 11, 2026 15:40
@spock-wen spock-wen reopened this Jun 11, 2026

@kittors kittors left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Request changes. The non-streaming guards are useful, but the streaming fix is incomplete.

In convertOpenAIStreamingChunkToAnthropic, an empty function.name no longer emits content_block_start, but the accumulator still stores arguments. When finish_reason=tool_calls or [DONE] is processed, the existing finalization path still iterates every accumulator and can emit input_json_delta/content_block_stop for a tool block that was never started. That leaves orphan Anthropic stream events.

It also does not adjust stop_reason when all tool calls were skipped: tool_calls/function_call can still become tool_use even though no valid tool_use block was emitted. Request-side handling also needs to avoid empty/whitespace tool_use, empty tools/tool_choice names, and orphan tool_result messages for skipped tool_use ids.

A complete fix should track whether a tool content block has actually started, finalize only started blocks, map tool_calls to end_turn when no valid tool was emitted, trim whitespace-only names, and add regression tests for empty-name streaming, usage chunks, arguments-before-name, mixed valid/empty tools, non-streaming, and request conversion. See #430/#431 for the fuller implementation direction.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants