wavetermdev/waveterm · error

part %d: %w

Error message

part %d: %w

What it means

This is a wrapper error: when convertFileAIMessagePart fails while processing part i, ConvertAIMessageToAnthropicChatMessage re-wraps the underlying error as "part %d: %w", preserving the original cause (missing data, unsupported protocol/media type, validation failure) and attributing it to the specific part index.

Source

Thrown at pkg/aiusechat/anthropic/anthropic-convertmessage.go:482

	}

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

	return &anthropicChatMessage{
		MessageId: aiMsg.MessageId,
		Role:      "user",
		Content:   contentBlocks,
	}, nil
}

// hasInlineData checks if the part has data available for inline use (either Data field or data URL)
func hasInlineData(part uctypes.AIMessagePart) bool {
	hasData := len(part.Data) > 0

View on GitHub (pinned to a4447c1563)

Solutions

  1. Use errors.As/errors.Is on the wrapped error to identify the root cause for the reported part index
  2. Inspect aiMsg.Parts[i] — confirm Type, Data/URL, and MimeType are consistent
  3. Fix the underlying cause per the inner error (add base64 data, use data: URL, correct media type)
  4. Pre-validate each file part (part.Validate() plus URL-scheme check) before conversion

Example fix

// before
msg, err := ConvertAIMessageToNativeChatMessage(aiMsg) // "part 2: no data available..."
// after
if err != nil {
    var idx int
    if _, e := fmt.Sscanf(err.Error(), "part %d:", &idx); e == nil {
        log.Printf("bad part %d: %+v", idx, aiMsg.Parts[idx])
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

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

Type guard

func filePartReady(p uctypes.AIMessagePart) bool {
    return p.Type == uctypes.AIMessagePartTypeFile && p.Validate() == nil &&
        (len(p.Data) > 0 || p.URL == "" || strings.HasPrefix(p.URL, "data:") || strings.HasPrefix(p.URL, "http"))
}

Try / catch

out, err := ConvertAIMessageToNativeChatMessage(aiMsg)
if err != nil {
    var partErr struct{ idx int }
    if n, _ := fmt.Sscanf(err.Error(), "part %d:", &partErr.idx); n == 1 {
        log.Printf("part %d failed: %v — dropping it", partErr.idx, err)
        return convertWithoutPart(aiMsg, partErr.idx)
    }
    return err
}

Prevention

When it happens

Trigger: Any AIMessagePart with Type file that fails inside convertFileAIMessagePart: Validate() failure, non-data:/http(s) URL scheme, no inline data for image/pdf, base64 extraction failure, etc., when called via ConvertAIMessageToAnthropicChatMessage.

Common situations: Debugging a batch-converted message and needing to know which attachment is bad; files with no Data and a non-data URL (e.g. https URL for pdf without inline data in contexts requiring it); parts built from untrusted input.

Related errors


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