wavetermdev/waveterm · error
invalid AIMessage: %w
Error message
invalid AIMessage: %w
What it means
ConvertAIMessageToAnthropicChatMessage first runs aiMsg.Validate() and wraps any validation failure as "invalid AIMessage". This means the AIMessage struct itself failed the library's own validation rules (e.g. missing required fields like MessageId, no parts, or invalid part structure), before any per-part conversion happens.
Source
Thrown at pkg/aiusechat/anthropic/anthropic-convertmessage.go:463
},
}, nil
} else {
// HTTP/HTTPS URL → not supported inline, would need to fetch
return nil, fmt.Errorf("dropping text/plain file with URL (must be fetched and converted to base64 or uploaded to Files API)")
}
default:
// Other media types → not supported inline, must upload and use file_id
return nil, fmt.Errorf("dropping file with unsupported media type '%s' (must be uploaded to Files API and sent as file_id)", p.MediaType)
}
}
// convertAIMessageToAnthropicChatMessage converts an AIMessage to anthropicChatMessage
// These messages are ALWAYS role "user"
func ConvertAIMessageToAnthropicChatMessage(aiMsg uctypes.AIMessage) (*anthropicChatMessage, error) {
if err := aiMsg.Validate(); err != nil {
return nil, fmt.Errorf("invalid AIMessage: %w", err)
}
var contentBlocks []anthropicMessageContentBlock
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, anthropicMessageContentBlock{
Type: "text",
Text: part.Text,
})
case uctypes.AIMessagePartTypeFile:
block, err := convertFileAIMessagePart(part)
if err != nil {View on GitHub (pinned to a4447c1563)
Solutions
- Call aiMsg.Validate() yourself before conversion and log the wrapped error to see the exact rule that failed
- Populate required fields (MessageId, at least one valid Part)
- Check that JSON unmarshaling into uctypes.AIMessage maps the expected fields (correct json tags)
- Re-run validation after upgrading the library in case Validate() requirements changed
Example fix
// before
msg := uctypes.AIMessage{Parts: []uctypes.AIMessagePart{{Type: uctypes.AIMessagePartTypeText, Text: "hi"}}}
out, err := ConvertAIMessageToAnthropicChatMessage(msg) // fails validation
// after
msg := uctypes.AIMessage{MessageId: "msg_123", Parts: []uctypes.AIMessagePart{{Type: uctypes.AIMessagePartTypeText, Text: "hi"}}}
if err := msg.Validate(); err != nil { log.Fatal(err) }
out, err := ConvertAIMessageToAnthropicChatMessage(msg) Defensive patterns
Strategy: validation
Validate before calling
if err := aiMsg.Validate(); err != nil {
return fmt.Errorf("AIMessage not convertible: %w", err)
}
out, err := ConvertAIMessageToAnthropicChatMessage(aiMsg) Type guard
func isValidAIMessage(m uctypes.AIMessage) bool {
return m.Validate() == nil
} Try / catch
out, err := ConvertAIMessageToAnthropicChatMessage(aiMsg)
if err != nil {
var verr *uctypes.ValidationError
if errors.As(err, &verr) {
log.Printf("invalid AIMessage (%s): %v", aiMsg.MessageId, verr)
return fallbackPlainMessage(aiMsg)
}
return err
} Prevention
- Call Validate() on messages right after construction/unmarshal, not just before sending
- Ensure JSON unmarshaling maps MessageId and Parts via correct tags
- Re-check validation rules after library upgrades
- Never pass zero-value AIMessage structs to converters
When it happens
Trigger: Calling ConvertAIMessageToAnthropicChatMessage (directly or via ConvertAIMessageToNativeChatMessage) with an AIMessage that has an empty Parts slice, an unset/invalid required field, or otherwise fails uctypes.AIMessage.Validate().
Common situations: Constructing AIMessage programmatically while skipping required fields; deserializing model output from JSON where MessageId/Parts were absent; passing a zero-value AIMessage{}; version drift where Validate() gained new required-field rules.
Related errors
- toolResults cannot be empty
- convertFileUIMessagePart expects 'file' type, got '%s'
- part %d: text type requires non-empty text field
- part %d: unsupported part type '%s'
- convertFileAIMessagePart expects 'file' type, got '%s'
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/a856087ef9f49508.
Report an issue: GitHub.