{"record":{"id":"6301e4e228b41134","repo":"wavetermdev/waveterm","slug":"unsupported-url-protocol-in-file-part-s","errorCode":null,"errorMessage":"unsupported URL protocol in file part: %s","messagePattern":"unsupported URL protocol in file part: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/aiutil/aiutil.go","lineNumber":74,"sourceCode":"}\n\n// GenerateDeterministicSuffix creates an 8-character hash from input strings\nfunc GenerateDeterministicSuffix(inputs ...string) string {\n\thasher := sha256.New()\n\tfor _, input := range inputs {\n\t\thasher.Write([]byte(input))\n\t}\n\thash := hasher.Sum(nil)\n\treturn hex.EncodeToString(hash)[:8]\n}\n\n// ExtractImageUrl extracts an image URL from either URL field (http/https/data) or raw Data\nfunc ExtractImageUrl(data []byte, url, mimeType string) (string, error) {\n\tif url != \"\" {\n\t\tif !strings.HasPrefix(url, \"data:\") &&\n\t\t\t!strings.HasPrefix(url, \"http://\") &&\n\t\t\t!strings.HasPrefix(url, \"https://\") {\n\t\t\treturn \"\", fmt.Errorf(\"unsupported URL protocol in file part: %s\", url)\n\t\t}\n\t\treturn url, nil\n\t}\n\tif len(data) > 0 {\n\t\tbase64Data := base64.StdEncoding.EncodeToString(data)\n\t\treturn fmt.Sprintf(\"data:%s;base64,%s\", mimeType, base64Data), nil\n\t}\n\treturn \"\", fmt.Errorf(\"file part missing both url and data\")\n}\n\n// ExtractTextData extracts text data from either Data field or URL field (data: URLs only)\nfunc ExtractTextData(data []byte, url string) ([]byte, error) {\n\tif len(data) > 0 {\n\t\treturn data, nil\n\t}\n\tif url != \"\" {\n\t\tif strings.HasPrefix(url, \"data:\") {\n\t\t\t_, decodedData, err := utilfn.DecodeDataURL(url)","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/aiutil/aiutil.go#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use http://, https://, or a data: URL for the part's URL field","For local files, load the bytes into the part's Data field and let ExtractImageUrl base64-encode them into a data URL","Normalize or reject unsupported-scheme URLs before building the AI message parts"],"exampleFix":"// before\npart := aiusechat.AIMessagePart{Type: \"image\", URL: \"file:///tmp/img.png\"}\n// after\ndata, _ := os.ReadFile(\"/tmp/img.png\")\npart := aiusechat.AIMessagePart{Type: \"image\", Data: data, MimeType: \"image/png\"}","handlingStrategy":"validation","validationCode":"func validImageURL(u string) bool {\n    return strings.HasPrefix(u, \"data:\") || strings.HasPrefix(u, \"http://\") || strings.HasPrefix(u, \"https://\")\n}","typeGuard":null,"tryCatchPattern":"url, err := aiutil.ExtractImageUrl(data, url, mimeType)\nif err != nil {\n    return fmt.Errorf(\"image part rejected: %w\", err)\n}","preventionTips":["Always use http/https/data URLs for AI image parts","Read local files into the Data field instead of file:// URLs","Validate URLs at message-construction time, before calling the provider"],"tags":["validation","url","ai"],"backgroundTag":"unsupported-url-protocol","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}