From 00822770ec40f06a4ec989017b697bf9979a9612 Mon Sep 17 00:00:00 2001 From: TinyCoder Date: Thu, 19 Feb 2026 16:43:10 +0700 Subject: [PATCH] fix(antigravity): prevent invalid JSON when tool_result has no content sjson.SetRaw with an empty string produces malformed JSON (e.g. "result":}). This happens when a Claude tool_result block has no content field, causing functionResponseResult.Raw to be "". Guard against this by falling back to sjson.Set with an empty string only when .Raw is empty. --- .../claude/antigravity_claude_request.go | 6 +- .../claude/antigravity_claude_request_test.go | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/internal/translator/antigravity/claude/antigravity_claude_request.go b/internal/translator/antigravity/claude/antigravity_claude_request.go index 65ad2b19..448aa976 100644 --- a/internal/translator/antigravity/claude/antigravity_claude_request.go +++ b/internal/translator/antigravity/claude/antigravity_claude_request.go @@ -231,8 +231,12 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ } else if functionResponseResult.IsObject() { functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", functionResponseResult.Raw) - } else { + } else if functionResponseResult.Raw != "" { functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", functionResponseResult.Raw) + } else { + // Content field is missing entirely — .Raw is empty which + // causes sjson.SetRaw to produce invalid JSON (e.g. "result":}). + functionResponseJSON, _ = sjson.Set(functionResponseJSON, "response.result", "") } partJSON := `{}` diff --git a/internal/translator/antigravity/claude/antigravity_claude_request_test.go b/internal/translator/antigravity/claude/antigravity_claude_request_test.go index 9f40b9fa..c28a14ec 100644 --- a/internal/translator/antigravity/claude/antigravity_claude_request_test.go +++ b/internal/translator/antigravity/claude/antigravity_claude_request_test.go @@ -661,6 +661,85 @@ func TestConvertClaudeRequestToAntigravity_ThinkingOnly_NoHint(t *testing.T) { } } +func TestConvertClaudeRequestToAntigravity_ToolResultNoContent(t *testing.T) { + // Bug repro: tool_result with no content field produces invalid JSON + inputJSON := []byte(`{ + "model": "claude-opus-4-6-thinking", + "messages": [ + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "MyTool-123-456", + "name": "MyTool", + "input": {"key": "value"} + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "MyTool-123-456" + } + ] + } + ] + }`) + + output := ConvertClaudeRequestToAntigravity("claude-opus-4-6-thinking", inputJSON, true) + outputStr := string(output) + + if !gjson.Valid(outputStr) { + t.Errorf("Result is not valid JSON:\n%s", outputStr) + } + + // Verify the functionResponse has a valid result value + fr := gjson.Get(outputStr, "request.contents.1.parts.0.functionResponse.response.result") + if !fr.Exists() { + t.Error("functionResponse.response.result should exist") + } +} + +func TestConvertClaudeRequestToAntigravity_ToolResultNullContent(t *testing.T) { + // Bug repro: tool_result with null content produces invalid JSON + inputJSON := []byte(`{ + "model": "claude-opus-4-6-thinking", + "messages": [ + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "MyTool-123-456", + "name": "MyTool", + "input": {"key": "value"} + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "MyTool-123-456", + "content": null + } + ] + } + ] + }`) + + output := ConvertClaudeRequestToAntigravity("claude-opus-4-6-thinking", inputJSON, true) + outputStr := string(output) + + if !gjson.Valid(outputStr) { + t.Errorf("Result is not valid JSON:\n%s", outputStr) + } +} + func TestConvertClaudeRequestToAntigravity_ToolAndThinking_NoExistingSystem(t *testing.T) { // When tools + thinking but no system instruction, should create one with hint inputJSON := []byte(`{