wavetermdev/waveterm · error

unsupported URL protocol in file part: %s

Error message

unsupported URL protocol in file part: %s

What it means

ExtractImageUrl in pkg/aiusechat/aiutil converts AI message file parts into data URLs. If a part carries a url that is not data:, http://, or https://, it is refused with "unsupported URL protocol in file part" because the AI providers only accept those schemes for image URLs.

Source

Thrown at pkg/aiusechat/aiutil/aiutil.go:74

}

// GenerateDeterministicSuffix creates an 8-character hash from input strings
func GenerateDeterministicSuffix(inputs ...string) string {
	hasher := sha256.New()
	for _, input := range inputs {
		hasher.Write([]byte(input))
	}
	hash := hasher.Sum(nil)
	return hex.EncodeToString(hash)[:8]
}

// ExtractImageUrl extracts an image URL from either URL field (http/https/data) or raw Data
func ExtractImageUrl(data []byte, url, mimeType string) (string, error) {
	if url != "" {
		if !strings.HasPrefix(url, "data:") &&
			!strings.HasPrefix(url, "http://") &&
			!strings.HasPrefix(url, "https://") {
			return "", fmt.Errorf("unsupported URL protocol in file part: %s", url)
		}
		return url, nil
	}
	if len(data) > 0 {
		base64Data := base64.StdEncoding.EncodeToString(data)
		return fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data), nil
	}
	return "", fmt.Errorf("file part missing both url and data")
}

// ExtractTextData extracts text data from either Data field or URL field (data: URLs only)
func ExtractTextData(data []byte, url string) ([]byte, error) {
	if len(data) > 0 {
		return data, nil
	}
	if url != "" {
		if strings.HasPrefix(url, "data:") {
			_, decodedData, err := utilfn.DecodeDataURL(url)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Use http://, https://, or a data: URL for the part's URL field
  2. For local files, load the bytes into the part's Data field and let ExtractImageUrl base64-encode them into a data URL
  3. Normalize or reject unsupported-scheme URLs before building the AI message parts

Example fix

// before
part := aiusechat.AIMessagePart{Type: "image", URL: "file:///tmp/img.png"}
// after
data, _ := os.ReadFile("/tmp/img.png")
part := aiusechat.AIMessagePart{Type: "image", Data: data, MimeType: "image/png"}
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

url, err := aiutil.ExtractImageUrl(data, url, mimeType)
if err != nil {
    return fmt.Errorf("image part rejected: %w", err)
}

Prevention

When it happens

Trigger: An AIMessagePart of type file/image whose URL field uses ftp://, file://, a relative path, or a protocol-relative //host URL; the check runs in ExtractImageUrl called from convertFileAIMessagePart / convertAIMessageMultimodal.

Common situations: Feeding local file paths as URLs instead of reading bytes into the Data field; passing URLs from vendor docs using unsupported schemes; upstream chat history containing non-http image links.

Related errors


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