wavetermdev/waveterm · error
part %d: text type requires non-empty text field
Error message
part %d: text type requires non-empty text field
What it means
Each text-type part of an AIMessage must carry a non-empty Text field. The library throws this when a part declares Type == AIMessagePartTypeText but part.Text is "", because an empty Anthropic text block would be rejected or meaningless.
Source
Thrown at pkg/aiusechat/anthropic/anthropic-convertmessage.go:472
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 {
return nil, fmt.Errorf("part %d: %w", i, err)
}
contentBlocks = append(contentBlocks, *block)
default:
return nil, fmt.Errorf("part %d: unsupported part type '%s'", i, part.Type)
}
}
View on GitHub (pinned to a4447c1563)
Solutions
- Ensure Text is non-empty before appending a text part, or omit the part entirely
- Check upstream sanitization/trimming that may empty the text
- Verify JSON field names map into part.Text during unmarshal
- Filter out empty-text parts before calling the converter
Example fix
// before
parts := append(parts, uctypes.AIMessagePart{Type: uctypes.AIMessagePartTypeText, Text: strings.TrimSpace(userInput)})
// after
if t := strings.TrimSpace(userInput); t != "" {
parts = append(parts, uctypes.AIMessagePart{Type: uctypes.AIMessagePartTypeText, Text: t})
} Defensive patterns
Strategy: validation
Validate before calling
for _, p := range aiMsg.Parts {
if p.Type == uctypes.AIMessagePartTypeText && p.Text == "" {
return errors.New("text part with empty text")
}
} Type guard
func hasNonEmptyText(p uctypes.AIMessagePart) bool {
return p.Type != uctypes.AIMessagePartTypeText || p.Text != ""
} Try / catch
if err := convert(aiMsg); err != nil {
if strings.Contains(err.Error(), "requires non-empty text") {
aiMsg.Parts = dropEmptyTextParts(aiMsg.Parts)
return convert(aiMsg)
}
return err
} Prevention
- Filter out empty-text parts before appending to Parts
- Beware Trim strings.TrimSpace results that can produce ""
- Check JSON field mapping so text actually lands in part.Text
When it happens
Trigger: Building an AIMessage with a part {Type: AIMessagePartTypeText} but leaving Text unset, or setting Text from a variable that is empty at conversion time; trimming user input to "" and still appending the part.
Common situations: Optional prompt fields that default to empty string; frontend sending {type:"text", text:""} placeholders; string-sanitization pipelines that strip all content; JSON payloads where the field is named "content" but unmarshals into Text incorrectly.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- invalid AIMessage: %w
- ai:model is required
- toolResults cannot be empty
- convertFileUIMessagePart expects 'file' type, got '%s'
- unsupported URL protocol in file part: %s
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/f60acc3d2dc63572.
Report an issue: GitHub.