{"record":{"id":"87484e107351c769","repo":"wavetermdev/waveterm","slug":"tool-result-missing-tooluseid-87484e","errorCode":null,"errorMessage":"tool result missing ToolUseID","messagePattern":"tool result missing ToolUseID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/openai/openai-convertmessage.go","lineNumber":467,"sourceCode":"\t\tMessageId: aiMsg.MessageId,\n\t\tMessage: &OpenAIMessage{\n\t\t\tRole:    \"user\",\n\t\t\tContent: contentBlocks,\n\t\t},\n\t}, nil\n}\n\n// ConvertToolResultsToOpenAIChatMessage converts AIToolResult slice to OpenAIChatMessage slice\nfunc ConvertToolResultsToOpenAIChatMessage(toolResults []uctypes.AIToolResult) ([]*OpenAIChatMessage, error) {\n\tif len(toolResults) == 0 {\n\t\treturn nil, errors.New(\"toolResults cannot be empty\")\n\t}\n\n\tvar messages []*OpenAIChatMessage\n\n\tfor _, result := range toolResults {\n\t\tif result.ToolUseID == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"tool result missing ToolUseID\")\n\t\t}\n\n\t\t// Create the function call output with result data\n\t\tvar outputData any\n\t\tif result.ErrorText != \"\" {\n\t\t\t// Marshal error output to string\n\t\t\terrorOutput := OpenAIFunctionCallErrorOutput{\n\t\t\t\tOk:    \"false\",\n\t\t\t\tError: result.ErrorText,\n\t\t\t}\n\t\t\terrorBytes, err := json.Marshal(errorOutput)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to marshal error output: %w\", err)\n\t\t\t}\n\t\t\toutputData = string(errorBytes)\n\t\t} else {\n\t\t\t// Check if text looks like an image data URL\n\t\t\tif strings.HasPrefix(result.Text, \"data:image/\") {","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/openai/openai-convertmessage.go#L449-L485","documentation":"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.","triggerScenarios":"Passing a []ToolResult to ConvertToolResultsToNativeChatMessage / ConvertToolResultsToOpenAIChatMessage where at least one entry has ToolUseID == \"\" — e.g. results created without recording the originating call's ID.","commonSituations":"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.","solutions":["Set ToolUseID from the corresponding assistant tool-call ID when building each result","Filter out results with empty ToolUseID before conversion if they are genuinely orphaned","Check the persistence layer isn't dropping the ToolUseID field on round-trip"],"exampleFix":"// before\nresults := []ToolResult{{Text: \"42\"}}\n// after\nresults := []ToolResult{{ToolUseID: toolCall.ID, Text: \"42\"}}","handlingStrategy":"validation","validationCode":"for i, r := range toolResults {\n    if r.ToolUseID == \"\" { return fmt.Errorf(\"tool result %d missing ToolUseID\", i) }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always copy the assistant tool-call ID into the result's ToolUseID","Validate results before persistence and again before conversion","Don't drop fields when serializing tool results"],"tags":["openai","tool-use","validation"],"backgroundTag":"missing-tool-use-id","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}