wavetermdev/waveterm · error

toolResults cannot be empty

Error message

toolResults cannot be empty

What it means

ConvertToolResultsToAnthropicChatMessage converts a slice of AIToolResult into an Anthropic chat message containing tool_result content blocks. The library rejects an empty slice because Anthropic API messages must carry at least one content block; there is nothing meaningful to send for zero tool results. The caller must guarantee at least one tool result before invoking this converter.

Source

Thrown at pkg/aiusechat/anthropic/anthropic-convertmessage.go:750

func convertMessageForAPI(msg anthropicInputMessage) anthropicInputMessage {
	// Create a copy of the message
	converted := anthropicInputMessage{
		Role:    msg.Role,
		Content: make([]anthropicMessageContentBlock, len(msg.Content)),
	}

	// Copy each content block and clean it (strips internal fields)
	for i, block := range msg.Content {
		converted.Content[i] = *block.Clean()
	}

	return converted
}

// ConvertToolResultsToAnthropicChatMessage converts AIToolResult slice to anthropicChatMessage
func ConvertToolResultsToAnthropicChatMessage(toolResults []uctypes.AIToolResult) (*anthropicChatMessage, error) {
	if len(toolResults) == 0 {
		return nil, errors.New("toolResults cannot be empty")
	}

	var contentBlocks []anthropicMessageContentBlock

	for _, result := range toolResults {
		if result.ToolUseID == "" {
			return nil, fmt.Errorf("tool result missing ToolUseID")
		}

		var content interface{}
		var isError bool

		if result.ErrorText != "" {
			content = result.ErrorText
			isError = true
		} else {
			// Check if text looks like an image data URL
			if strings.HasPrefix(result.Text, "data:image/") {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Guard the call site: only call ConvertToolResultsToAnthropicChatMessage when len(toolResults) > 0
  2. If no tool results exist, skip the tool-result turn entirely and send the next user/assistant message instead
  3. If tool calls were made but not executed, run them (or synthesize tool_result blocks with an error payload) so the slice is non-empty
  4. Check upstream logic that builds the toolResults slice for early-return or filtering bugs that drop all entries

Example fix

// before
msg, err := ConvertToolResultsToAnthropicChatMessage(toolResults)
// after
if len(toolResults) == 0 {
    return nil // nothing to send; skip tool-result turn
}
msg, err := ConvertToolResultsToAnthropicChatMessage(toolResults)
Defensive patterns

Strategy: validation

Validate before calling

if len(toolResults) == 0 {
    // skip the tool-result turn entirely
    return nil
}
msg, err := ConvertToolResultsToAnthropicChatMessage(toolResults)

Prevention

When it happens

Trigger: Calling ConvertToolResultsToAnthropicChatMessage(nil) or ConvertToolResultsToAnthropicChatMessage([]uctypes.AIToolResult{}) — i.e. the len(toolResults) == 0 check at line 750. This happens when a tool-execution loop produced no results (e.g. all tool calls were filtered out or execution was skipped) but the code still proceeds to convert.

Common situations: A chat step ends with pending tool calls that were never executed; a batching layer collects results into a slice that ends up empty after deduplication/filtering; tests pass an empty slice; an early error path skipped tool execution but conversion is still attempted.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/bae5f095f26cef7d. Report an issue: GitHub.