wavetermdev/waveterm · error

tool result missing ToolUseID

Error message

tool result missing ToolUseID

What it means

ConvertToolResultsToOpenAIChatMessage requires every tool result to carry the ToolUseID that links it back to the assistant's tool call; if result.ToolUseID is empty it cannot emit the function_call_output, so it fails fast with this error.

Source

Thrown at pkg/aiusechat/openai/openai-convertmessage.go:467

		MessageId: aiMsg.MessageId,
		Message: &OpenAIMessage{
			Role:    "user",
			Content: contentBlocks,
		},
	}, nil
}

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

	var messages []*OpenAIChatMessage

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

		// Create the function call output with result data
		var outputData any
		if result.ErrorText != "" {
			// Marshal error output to string
			errorOutput := OpenAIFunctionCallErrorOutput{
				Ok:    "false",
				Error: result.ErrorText,
			}
			errorBytes, err := json.Marshal(errorOutput)
			if err != nil {
				return nil, fmt.Errorf("failed to marshal error output: %w", err)
			}
			outputData = string(errorBytes)
		} 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. Set ToolUseID from the corresponding assistant tool-call ID when building each result
  2. Filter out results with empty ToolUseID before conversion if they are genuinely orphaned
  3. Check the persistence layer isn't dropping the ToolUseID field on round-trip

Example fix

// before
results := []ToolResult{{Text: "42"}}
// after
results := []ToolResult{{ToolUseID: toolCall.ID, Text: "42"}}
Defensive patterns

Strategy: validation

Validate before calling

for i, r := range toolResults {
    if r.ToolUseID == "" { return fmt.Errorf("tool result %d missing ToolUseID", i) }
}

Prevention

When it happens

Trigger: Passing a []ToolResult to ConvertToolResultsToNativeChatMessage / ConvertToolResultsToOpenAIChatMessage where at least one entry has ToolUseID == "" — e.g. results created without recording the originating call's ID.

Common situations: Hand-building tool results in tests; dropping the ID when persisting/reloading tool results from a store; constructing results from a tool executor that isn't given the call ID.

Related errors


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