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

  1. Ensure Text is non-empty before appending a text part, or omit the part entirely
  2. Check upstream sanitization/trimming that may empty the text
  3. Verify JSON field names map into part.Text during unmarshal
  4. 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

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


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