From 4e3bad39075a0ca82c5da77aba09cfc1bfae0521 Mon Sep 17 00:00:00 2001 From: taetaetae Date: Fri, 6 Feb 2026 11:58:43 +0900 Subject: [PATCH] fix(kiro): handle empty content in current user message for compaction Problem: - PR #186 fixed empty content for assistant messages and history user messages - But current user message (isLastMessage == true) was not fixed - When user message contains only tool_result (no text), content becomes empty - This causes 'Improperly formed request' errors from Kiro API - Compaction requests from OpenCode commonly have this pattern Solution: - Move empty content check BEFORE the isLastMessage branch - Apply fallback content to ALL user messages, not just history - Add DefaultUserContentWithToolResults and DefaultUserContent constants Fixes compaction failures for OpenCode + Quotio + CLIProxyAPIPlus + Kiro stack --- .../kiro/claude/kiro_claude_request.go | 20 +++++++++++-------- internal/translator/kiro/common/constants.go | 8 ++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/internal/translator/kiro/claude/kiro_claude_request.go b/internal/translator/kiro/claude/kiro_claude_request.go index 259ae9f5..425d9ae2 100644 --- a/internal/translator/kiro/claude/kiro_claude_request.go +++ b/internal/translator/kiro/claude/kiro_claude_request.go @@ -608,18 +608,22 @@ func processMessages(messages gjson.Result, modelID, origin string) ([]KiroHisto if role == "user" { userMsg, toolResults := BuildUserMessageStruct(msg, modelID, origin) + // CRITICAL: Kiro API requires content to be non-empty for ALL user messages + // This includes both history messages and the current message. + // When user message contains only tool_result (no text), content will be empty. + // This commonly happens in compaction requests from OpenCode. + if strings.TrimSpace(userMsg.Content) == "" { + if len(toolResults) > 0 { + userMsg.Content = kirocommon.DefaultUserContentWithToolResults + } else { + userMsg.Content = kirocommon.DefaultUserContent + } + log.Debugf("kiro: user content was empty, using default: %s", userMsg.Content) + } if isLastMessage { currentUserMsg = &userMsg currentToolResults = toolResults } else { - // CRITICAL: Kiro API requires content to be non-empty for history messages too - if strings.TrimSpace(userMsg.Content) == "" { - if len(toolResults) > 0 { - userMsg.Content = "Tool results provided." - } else { - userMsg.Content = "Continue" - } - } // For history messages, embed tool results in context if len(toolResults) > 0 { userMsg.UserInputMessageContext = &KiroUserInputMessageContext{ diff --git a/internal/translator/kiro/common/constants.go b/internal/translator/kiro/common/constants.go index ab000972..4477864a 100644 --- a/internal/translator/kiro/common/constants.go +++ b/internal/translator/kiro/common/constants.go @@ -37,6 +37,14 @@ const ( // that have no content at all. Kiro API requires non-empty content. DefaultAssistantContent = "I understand." + // DefaultUserContentWithToolResults is the fallback content for user messages + // that have only tool_result (no text). Kiro API requires non-empty content. + DefaultUserContentWithToolResults = "Tool results provided." + + // DefaultUserContent is the fallback content for user messages + // that have no content at all. Kiro API requires non-empty content. + DefaultUserContent = "Continue" + // KiroAgenticSystemPrompt is injected only for -agentic models to prevent timeouts on large writes. // AWS Kiro API has a 2-3 minute timeout for large file write operations. KiroAgenticSystemPrompt = `