wavetermdev/waveterm · error

invalid AIMessage: %w

Error message

invalid AIMessage: %w

What it means

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).

Source

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

		formattedText := aiutil.FormatAttachedDirectoryListing(part.FileName, jsonContent)

		return &OpenAIMessageContent{
			Type: "input_text",
			Text: formattedText,
		}, nil

	default:
		return nil, fmt.Errorf("dropping file with unsupported mimetype '%s' (OpenAI supports images, PDFs, text/plain, and directories)", part.MimeType)
	}
}

// ConvertAIMessageToOpenAIChatMessage converts an AIMessage to OpenAIChatMessage
// These messages are ALWAYS role "user"
// Handles text parts, images, PDFs, and text/plain files
func ConvertAIMessageToOpenAIChatMessage(aiMsg uctypes.AIMessage) (*OpenAIChatMessage, error) {
	if err := aiMsg.Validate(); err != nil {
		return nil, fmt.Errorf("invalid AIMessage: %w", err)
	}

	var contentBlocks []OpenAIMessageContent
	imageCount := 0
	imageFailCount := 0

	for i, part := range aiMsg.Parts {
		switch part.Type {
		case uctypes.AIMessagePartTypeText:
			if part.Text == "" {
				return nil, fmt.Errorf("part %d: text type requires non-empty text field", i)
			}
			contentBlocks = append(contentBlocks, OpenAIMessageContent{
				Type: "input_text",
				Text: part.Text,
			})

		case uctypes.AIMessagePartTypeFile:

View on GitHub (pinned to a4447c1563)

Solutions

  1. Log/print the wrapped inner error from Validate() to see which field is invalid
  2. Fix the AIMessage construction to populate all fields required by Validate() (role, parts, etc.)
  3. If loading from storage, re-validate after unmarshal and repair or drop invalid messages
  4. Use the library's constructors/helpers for AIMessage instead of raw struct literals

Example fix

// before
msg := uctypes.AIMessage{}
out, err := ConvertAIMessageToOpenAIChatMessage(msg) // invalid AIMessage: ...
// after
msg := uctypes.AIMessage{Role: uctypes.RoleUser, Parts: []uctypes.AIMessagePart{{Type: uctypes.AIMessagePartTypeText, Text: "hello"}}}
if err := msg.Validate(); err != nil { return err }
out, err := ConvertAIMessageToOpenAIChatMessage(msg)
Defensive patterns

Strategy: validation

Validate before calling

if err := aiMsg.Validate(); err != nil {
    return fmt.Errorf("cannot convert message: %w", err)
}

Type guard

func isValidAIMessage(msg uctypes.AIMessage) bool { return msg.Validate() == nil }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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