{"record":{"id":"ca64326ae34b5f85","repo":"wavetermdev/waveterm","slug":"invalid-aimessage-w-ca6432","errorCode":null,"errorMessage":"invalid AIMessage: %w","messagePattern":"invalid AIMessage: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/openai/openai-convertmessage.go","lineNumber":402,"sourceCode":"\n\t\tformattedText := aiutil.FormatAttachedDirectoryListing(part.FileName, jsonContent)\n\n\t\treturn &OpenAIMessageContent{\n\t\t\tType: \"input_text\",\n\t\t\tText: formattedText,\n\t\t}, nil\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"dropping file with unsupported mimetype '%s' (OpenAI supports images, PDFs, text/plain, and directories)\", part.MimeType)\n\t}\n}\n\n// ConvertAIMessageToOpenAIChatMessage converts an AIMessage to OpenAIChatMessage\n// These messages are ALWAYS role \"user\"\n// Handles text parts, images, PDFs, and text/plain files\nfunc ConvertAIMessageToOpenAIChatMessage(aiMsg uctypes.AIMessage) (*OpenAIChatMessage, error) {\n\tif err := aiMsg.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid AIMessage: %w\", err)\n\t}\n\n\tvar contentBlocks []OpenAIMessageContent\n\timageCount := 0\n\timageFailCount := 0\n\n\tfor i, part := range aiMsg.Parts {\n\t\tswitch part.Type {\n\t\tcase uctypes.AIMessagePartTypeText:\n\t\t\tif part.Text == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"part %d: text type requires non-empty text field\", i)\n\t\t\t}\n\t\t\tcontentBlocks = append(contentBlocks, OpenAIMessageContent{\n\t\t\t\tType: \"input_text\",\n\t\t\t\tText: part.Text,\n\t\t\t})\n\n\t\tcase uctypes.AIMessagePartTypeFile:","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/openai/openai-convertmessage.go#L384-L420","documentation":"ConvertAIMessageToOpenAIChatMessage first validates the incoming uctypes.AIMessage; if aiMsg.Validate() returns an error the message cannot be safely converted to an OpenAI chat message, so the function aborts and wraps the validation error with this prefix. The wrapped inner error describes the exact structural problem (e.g. missing role or parts).","triggerScenarios":"Calling ConvertAIMessageToNativeChatMessage (or ConvertAIMessageToOpenAIChatMessage directly) with an AIMessage whose Validate() fails — typically an AIMessage constructed programmatically with missing/invalid required fields instead of one produced by the chat pipeline.","commonSituations":"Hand-constructing AIMessage structs in tests or tools and forgetting required fields; deserializing stored chats where fields were dropped; passing a zero-value AIMessage{} after a failed unmarshal.","solutions":["Log/print the wrapped inner error from Validate() to see which field is invalid","Fix the AIMessage construction to populate all fields required by Validate() (role, parts, etc.)","If loading from storage, re-validate after unmarshal and repair or drop invalid messages","Use the library's constructors/helpers for AIMessage instead of raw struct literals"],"exampleFix":"// before\nmsg := uctypes.AIMessage{}\nout, err := ConvertAIMessageToOpenAIChatMessage(msg) // invalid AIMessage: ...\n// after\nmsg := uctypes.AIMessage{Role: uctypes.RoleUser, Parts: []uctypes.AIMessagePart{{Type: uctypes.AIMessagePartTypeText, Text: \"hello\"}}}\nif err := msg.Validate(); err != nil { return err }\nout, err := ConvertAIMessageToOpenAIChatMessage(msg)","handlingStrategy":"validation","validationCode":"if err := aiMsg.Validate(); err != nil {\n    return fmt.Errorf(\"cannot convert message: %w\", err)\n}","typeGuard":"func isValidAIMessage(msg uctypes.AIMessage) bool { return msg.Validate() == nil }","tryCatchPattern":null,"preventionTips":["Always call Validate() on AIMessage before conversion","Use library constructors rather than raw struct literals","Validate after deserializing stored chats"],"tags":["validation","openai","message-conversion"],"backgroundTag":"message-validation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}