wavetermdev/waveterm · error

unsupported URL protocol in file part: %s

Error message

unsupported URL protocol in file part: %s

What it means

The Anthropic converter only accepts file-part URLs with a data:, http://, or https:// scheme. Any other protocol (ftp://, file://, blob:, ws:, or a bare path like /tmp/x.png) is rejected before any block is built, because the library will not fetch arbitrary schemes or guess a source type. For AIMessageParts (unlike UIMessageParts) this check only applies when a URL is present at all.

Source

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

}

// 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/"):
		// image/* (jpeg, png, gif, webp) → Anthropic image block
		if strings.HasPrefix(p.URL, "data:") {
			// Data URL → base64 source
			parts := strings.SplitN(p.URL, ",", 2)
			if len(parts) != 2 {
				return nil, errors.New("invalid data URL format")
			}
			return &anthropicMessageContentBlock{
				Type: "image",
				Source: &anthropicSource{
					Type:      "base64",
					Data:      parts[1],
					MediaType: p.MediaType,

View on GitHub (pinned to a4447c1563)

Solutions

  1. Convert the file content to a data: URL (base64) before building the part
  2. Serve the file over public http(s) and use that URL instead
  3. Upload to the Anthropic Files API and reference by file_id if supported by your flow
  4. Strip or fix blob:/file:/custom-scheme URLs when ingesting parts from the frontend

Example fix

// before
part := uctypes.UIMessagePart{Type: "file", URL: "file:///tmp/report.pdf", MediaType: "application/pdf"}
// after
data, _ := os.ReadFile("/tmp/report.pdf")
part := uctypes.UIMessagePart{Type: "file", URL: "data:application/pdf;base64," + base64.StdEncoding.EncodeToString(data), MediaType: "application/pdf"}
Defensive patterns

Strategy: validation

Validate before calling

func validURLScheme(u string) bool {
    return strings.HasPrefix(u, "data:") || strings.HasPrefix(u, "http://") || strings.HasPrefix(u, "https://")
}
if part.URL != "" && !validURLScheme(part.URL) { return fmt.Errorf("unsupported scheme: %s", part.URL) }

Type guard

func isFetchableOrDataURL(u string) bool {
    return strings.HasPrefix(u, "data:") || strings.HasPrefix(u, "http://") || strings.HasPrefix(u, "https://")
}

Try / catch

if err := convertAndSend(part); err != nil {
    var protoErr = "unsupported URL protocol"
    if strings.Contains(err.Error(), protoErr) {
        // fall back: fetch bytes and resend as data: URL
        return resendAsDataURL(part)
    }
    return err
}

Prevention

When it happens

Trigger: Setting p.URL to an unsupported scheme such as file:///home/user/doc.pdf, ftp://..., blob:..., or a bare local path and then sending the message through ConvertUIMessageToAnthropicChatMessage / convertFileUIMessagePart.

Common situations: Server-side code reusing browser blob: URLs that are meaningless outside the client; passing local filesystem paths as URLs; generating presigned or internal scheme URLs (s3://, gs://) that Anthropic cannot consume.

Related errors


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