Skip to content

bug: Error 400 -4316 when user manually aborts gcp-claude4-sonnet model and continues messaging #1040

@ulivz

Description

@ulivz

Summary

After aborting an operation and continuing the conversation, the following error occurs:

Error in agent execution: Error: 400 -4316: messages.37: all messages must have non-empty content except for the optional final assistant message

Root Cause

When abort occurs during LLM streaming, an empty assistant message is created in conversation history:

First Agent Loop (Normal)

[
  {"role": "system", "content": "..."},
  {"role": "user", "content": "Question"}
]

Second Agent Loop (After Abort)

[
  {"role": "system", "content": "..."},
  {"role": "user", "content": "Question"},
  {"role": "assistant"}, // Empty content violates Claude API validation
  {"role": "user", "content": "继续"}
]

Technical Analysis

The issue occurs in llm-processor.ts createFinalEvents method:

if (content || currentToolCalls.length > 0) {
  const assistantEvent = this.eventStream.createEvent('assistant_message', {
    content: content, // Empty string when aborted
    finishReason: finishReason,
    // ...
  });
}

When NativeToolCallEngine.buildHistoricalAssistantMessage processes this:

const message: ChatCompletionMessageParam = {
  role: 'assistant',
  content: content, // Empty string violates Claude API
};

Solution

Modify createFinalEvents in llm-processor.ts:

private createFinalEvents(
  content: string,
  rawContent: string,
  currentToolCalls: ChatCompletionMessageToolCall[],
  reasoningBuffer: string,
  finishReason: string,
  messageId?: string,
  ttftMs?: number,
  ttltMs?: number,
): void {
  // Provide default content for aborted requests
  let finalContent = content;
  if (!content && finishReason === 'abort') {
    finalContent = '[Request was aborted by user]';
  }

  if (finalContent || currentToolCalls.length > 0) {
    const assistantEvent = this.eventStream.createEvent('assistant_message', {
      content: finalContent,
      rawContent: rawContent,
      toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,
      finishReason: finishReason,
      messageId: messageId,
      ttftMs: ttftMs,
      ttltMs: ttltMs,
    });

    this.eventStream.sendEvent(assistantEvent);
  }
}

Benefits:

  • Maintains conversation continuity
  • Provides meaningful context to LLM
  • Fixes API validation error
  • Minimal code change

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions