wavetermdev/waveterm · error

parts must not be empty

Error message

parts must not be empty

What it means

AIMessage.Validate requires at least one part; a message with an empty Parts slice is structurally meaningless in the UI message protocol and is rejected. This guarantees downstream consumers never iterate zero parts when rendering or processing the conversation.

Source

Thrown at pkg/aiusechat/uctypes/uctypes.go:385

type AIToolResult struct {
	ToolName  string `json:"toolname"`
	ToolUseID string `json:"tooluseid"`
	ErrorText string `json:"errortext,omitempty"`
	Text      string `json:"text,omitempty"`
}

func (m *AIMessage) GetMessageId() string {
	return m.MessageId
}

func (m *AIMessage) Validate() error {
	if m.MessageId == "" {
		return fmt.Errorf("messageid must be set")
	}

	if len(m.Parts) == 0 {
		return fmt.Errorf("parts must not be empty")
	}

	for i, part := range m.Parts {
		if err := part.Validate(); err != nil {
			return fmt.Errorf("part %d: %w", i, err)
		}
	}

	return nil
}

func (p *AIMessagePart) Validate() error {
	if p.Type == AIMessagePartTypeText {
		if p.Text == "" {
			return fmt.Errorf("text type requires non-empty text field")
		}
		// Check that no file fields are set
		if p.FileName != "" || p.MimeType != "" || len(p.Data) > 0 || p.URL != "" {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Append at least one valid part (text/tool-use/etc.) before validating
  2. If the message is a streaming placeholder, defer Validate() until parts are populated
  3. Check upstream filtering that may be stripping all parts
  4. Fix the serializer so parts are not lost (e.g. omit-empty on the wrong field)

Example fix

// before
msg := &uctypes.AIMessage{MessageId: id}  // no parts
err := msg.Validate() // "parts must not be empty"
// after
msg := &uctypes.AIMessage{MessageId: id, Parts: []uctypes.MessagePart{uctypes.NewTextPart("hello")}}
err := msg.Validate()
Defensive patterns

Strategy: validation

Validate before calling

if len(msg.Parts) == 0 {
    return fmt.Errorf("AIMessage %s requires at least one part", msg.MessageId)
}

Type guard

func hasParts(m *uctypes.AIMessage) bool {
    return m != nil && len(m.Parts) > 0
}

Try / catch

if err := msg.Validate(); err != nil {
    if strings.Contains(err.Error(), "parts must not be empty") {
        // defer validation until parts are streamed in, or append a placeholder part
    }
    return err
}

Prevention

When it happens

Trigger: Validate() on an AIMessage with MessageId set but len(Parts) == 0 — e.g. deserializing {"messageid":"x","parts":[]}, dropping all parts during transformation/filtering, or building a message before appending content.

Common situations: Streaming code that creates the message shell before parts arrive and validates too early; filtering logic that removes all invalid parts; serializers that omit empty slices and round-trip to empty.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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