wavetermdev/waveterm · error

convertFileUIMessagePart expects 'file' type, got '%s'

Error message

convertFileUIMessagePart expects 'file' type, got '%s'

What it means

convertFileUIMessagePart is an internal dispatcher that only handles UIMessageParts whose Type is exactly "file". The library throws this error when a caller (via convertPartToAnthropicBlocks) routes a part of a different type into the file conversion path, indicating an internal mis-dispatch or a caller constructing parts with a wrong/empty Type field. It is a defensive type check, not a data-content validation.

Source

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

func convertPartsToAnthropicBlocks(parts []uctypes.UIMessagePart, role string) ([]anthropicMessageContentBlock, error) {
	var blocks []anthropicMessageContentBlock

	for _, p := range parts {
		partBlocks, err := convertPartToAnthropicBlocks(p, role, len(blocks))
		if err != nil {
			log.Printf("anthropic: %v", err)
			continue
		}
		blocks = append(blocks, partBlocks...)
	}

	return blocks, nil
}

// convertFileUIMessagePart converts a file part to Anthropic image or document block format
func convertFileUIMessagePart(p uctypes.UIMessagePart) (*anthropicMessageContentBlock, error) {
	if p.Type != "file" {
		return nil, fmt.Errorf("convertFileUIMessagePart expects 'file' type, got '%s'", p.Type)
	}
	if p.URL == "" {
		return nil, errors.New("file part missing url")
	}
	if p.MediaType == "" {
		return nil, errors.New("file part missing mediaType")
	}

	// Validate URL protocol - only allow data:, http:, https:
	if !strings.HasPrefix(p.URL, "data:") &&
		!strings.HasPrefix(p.URL, "http://") &&
		!strings.HasPrefix(p.URL, "https://") {
		return nil, fmt.Errorf("unsupported URL protocol in file part: %s", p.URL)
	}

	// Branch on mediaType first to determine block type and constraints
	switch {
	case strings.HasPrefix(p.MediaType, "image/"):

View on GitHub (pinned to a4447c1563)

Solutions

  1. Set the part's Type field to "file" (uctypes.UIMessagePartTypeFile) before adding it to the message
  2. Check that your code routes only file parts into the file conversion path (fix the dispatch in convertPartToAnthropicBlocks callers)
  3. Log/inspect p.Type at the call site to find where the mis-typed part originates

Example fix

// before
part := uctypes.UIMessagePart{URL: "data:text/plain;base64,aGk=", MediaType: "text/plain"}
// after
part := uctypes.UIMessagePart{Type: "file", URL: "data:text/plain;base64,aGk=", MediaType: "text/plain"}
Defensive patterns

Strategy: type-guard

Validate before calling

func isFileUIPart(p uctypes.UIMessagePart) bool { return p.Type == "file" }
if !isFileUIPart(p) { return fmt.Errorf("skipping non-file part %q", p.Type) }

Type guard

func isFileUIPart(p uctypes.UIMessagePart) bool {
    return p.Type == "file"
}

Try / catch

block, err := convertFileUIMessagePart(p)
if err != nil {
    if strings.Contains(err.Error(), "expects 'file' type") {
        log.Printf("mis-typed part %q skipped", p.Type)
        return nil, nil // skip
    }
    return nil, err
}

Prevention

When it happens

Trigger: Passing a UIMessagePart with Type set to something other than "file" (e.g. "text", "image", "", or a custom type) such that convertPartToAnthropicBlocks dispatches it to convertFileUIMessagePart.

Common situations: Building message parts by hand without setting Type; deserializing UI messages from JSON where the type discriminator was lost or renamed; a mapping bug in the caller that sends non-file parts down the file branch.

Related errors


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