{"record":{"id":"527b2885637c03d2","repo":"wavetermdev/waveterm","slug":"dropping-text-plain-file-with-url-must-be-fetched","errorCode":null,"errorMessage":"dropping text/plain file with URL (must be fetched and converted to data)","messagePattern":"dropping text/plain file with URL \\(must be fetched and converted to data\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/aiusechat/aiutil/aiutil.go","lineNumber":98,"sourceCode":"\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)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to decode data URL for text/plain file: %w\", err)\n\t\t\t}\n\t\t\treturn decodedData, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"dropping text/plain file with URL (must be fetched and converted to data)\")\n\t}\n\treturn nil, fmt.Errorf(\"text/plain file part missing data\")\n}\n\n// FormatAttachedTextFile formats a text file attachment with proper encoding and deterministic suffix\nfunc FormatAttachedTextFile(fileName string, textContent []byte) string {\n\tif fileName == \"\" {\n\t\tfileName = \"untitled.txt\"\n\t}\n\n\tencodedFileName := strings.ReplaceAll(fileName, `\"`, \"&quot;\")\n\tquotedFileName := strconv.Quote(encodedFileName)\n\n\ttextStr := string(textContent)\n\tdeterministicSuffix := GenerateDeterministicSuffix(textStr, fileName)\n\treturn fmt.Sprintf(\"<AttachedTextFile_%s file_name=%s>\\n%s\\n</AttachedTextFile_%s>\", deterministicSuffix, quotedFileName, textStr, deterministicSuffix)\n}\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/aiutil/aiutil.go#L80-L116","documentation":"ExtractTextData only accepts text content as raw bytes or as a data: URL. If the url is a remote reference (http/https or other scheme) the function refuses to process it because it does not fetch network resources. The text file is effectively 'dropped' from the message.","triggerScenarios":"Passing ExtractTextData a URL like \"https://example.com/notes.txt\" or any non-data: URL with empty data. Callers (convertFileAIMessagePart, convertAIMessageTextOnly, convertAIMessageMultimodal) hit this when an attached text file part carries a remote URL instead of inline content.","commonSituations":"Client uploads a file reference/link rather than file content; frontend integration forwards a CDN URL for text files; migrating from providers that accept remote file URLs to this message format that requires inline data.","solutions":["Fetch the URL yourself (http.Get or equivalent) and pass the response bytes via the data parameter.","Convert the fetched bytes to a data: URL before passing, if you must keep the URL parameter shape.","Change the client to upload the text content inline (base64 data URL) instead of sending a link.","Handle the error by dropping the attachment and appending a note to the message text."],"exampleFix":"// before\ncontent, err := aiutil.ExtractTextData(nil, \"https://example.com/notes.txt\") // dropped\n// after\nresp, _ := http.Get(\"https://example.com/notes.txt\")\nbody, _ := io.ReadAll(resp.Body)\ncontent, err := aiutil.ExtractTextData(body, \"\")","handlingStrategy":"fallback","validationCode":"func needsFetch(u string) bool { return u != \"\" && !strings.HasPrefix(u, \"data:\") }","typeGuard":"func isDataURL(u string) bool { return strings.HasPrefix(u, \"data:\") }","tryCatchPattern":"content, err := aiutil.ExtractTextData(data, url)\nif err != nil && strings.Contains(err.Error(), \"dropping text/plain\") {\n    resp, ferr := http.Get(url)\n    if ferr == nil {\n        body, _ := io.ReadAll(resp.Body)\n        content, err = aiutil.ExtractTextData(body, \"\")\n    }\n}","preventionTips":["Fetch remote text URLs into bytes before attaching them to messages","Prefer inline data: URLs for text files in client integrations","Document to frontend authors that text attachments must carry content, not links"],"tags":["text-attachment","remote-url","unsupported-operation"],"backgroundTag":"remote-url-not-supported","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}