{"record":{"id":"7a871a769622b270","repo":"wavetermdev/waveterm","slug":"dropping-text-plain-file-with-url-must-be-fetched-7a871a","errorCode":null,"errorMessage":"dropping text/plain file with URL (must be fetched and converted to base64 or uploaded to Files API)","messagePattern":"dropping text/plain file with URL \\(must be fetched and converted to base64 or uploaded to Files API\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/anthropic/anthropic-convertmessage.go","lineNumber":449,"sourceCode":"\t\t\tif len(parts) != 2 {\n\t\t\t\treturn nil, errors.New(\"invalid data URL format\")\n\t\t\t}\n\t\t\t// Decode base64 data\n\t\t\ttextData, err := base64.StdEncoding.DecodeString(parts[1])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to decode base64 data: %w\", err)\n\t\t\t}\n\t\t\treturn &anthropicMessageContentBlock{\n\t\t\t\tType: \"document\",\n\t\t\t\tSource: &anthropicSource{\n\t\t\t\t\tType:      \"text\",\n\t\t\t\t\tData:      string(textData),\n\t\t\t\t\tMediaType: \"text/plain\",\n\t\t\t\t},\n\t\t\t}, nil\n\t\t} else {\n\t\t\t// HTTP/HTTPS URL → not supported inline, would need to fetch\n\t\t\treturn nil, fmt.Errorf(\"dropping text/plain file with URL (must be fetched and converted to base64 or uploaded to Files API)\")\n\t\t}\n\n\tdefault:\n\t\t// Other media types → not supported inline, must upload and use file_id\n\t\treturn nil, fmt.Errorf(\"dropping file with unsupported media type '%s' (must be uploaded to Files API and sent as file_id)\", p.MediaType)\n\t}\n\n}\n\n// convertAIMessageToAnthropicChatMessage converts an AIMessage to anthropicChatMessage\n// These messages are ALWAYS role \"user\"\nfunc ConvertAIMessageToAnthropicChatMessage(aiMsg uctypes.AIMessage) (*anthropicChatMessage, error) {\n\tif err := aiMsg.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid AIMessage: %w\", err)\n\t}\n\n\tvar contentBlocks []anthropicMessageContentBlock\n","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/anthropic/anthropic-convertmessage.go#L431-L467","documentation":"text/plain file parts are only supported when embedded inline as a data: URL (decoded to an Anthropic plain-text document source). The Anthropic API has no URL-based text document form this converter can use, so a text/plain part carrying an http(s) URL is rejected with this error telling you to fetch the content yourself and inline it, or upload via the Files API.","triggerScenarios":"Creating a UIMessagePart{Type:\"file\", MediaType:\"text/plain\", URL:\"https://...\"} and sending it through ConvertUIMessageToAnthropicChatMessage — the error fires in the else branch at anthropic-convertmessage.go:447-450.","commonSituations":"Pointing at a raw .txt/.md/.csv file hosted on S3/CDN; passing a chat file attachment that the client stored as an http URL; migrating from OpenAI-style converters where URL text files were accepted.","solutions":["Fetch the URL server-side, then send the content as a data:text/plain;base64,... data URL","Upload the file to the Anthropic Files API and reference it by file_id","Inline the text directly as a text part (uctypes text part) instead of a file part"],"exampleFix":"// before\npart := uctypes.UIMessagePart{Type: \"file\", MediaType: \"text/plain\", URL: \"https://cdn.example.com/notes.txt\"}\n// after\nbody, _ := http.Get(part.URL) // fetch and inline\nb64 := base64.StdEncoding.EncodeToString(body)\npart := uctypes.UIMessagePart{Type: \"file\", MediaType: \"text/plain\", URL: \"data:text/plain;base64,\" + b64}","handlingStrategy":"fallback","validationCode":"if p.MediaType == \"text/plain\" && !strings.HasPrefix(p.URL, \"data:\") {\n    // fetch and inline, or convert to a text part, before calling the API\n}","typeGuard":"func isSupportedTextFilePart(p uctypes.UIMessagePart) bool {\n    return p.MediaType != \"text/plain\" || strings.HasPrefix(p.URL, \"data:\")\n}","tryCatchPattern":"if err := convert(msg); err != nil {\n    if strings.Contains(err.Error(), \"dropping text/plain file with URL\") {\n        body, ferr := http.Get(p.URL)\n        if ferr == nil { return convert(withDataURL(msg, body)) }\n    }\n    return err\n}","preventionTips":["Pre-fetch any http(s) text file server-side and inline it as a data URL","Prefer a plain text part over a text/plain file part for hosted text content","Keep a small pre-flight validator mirroring the converter's media-type/URL rules"],"tags":["go","anthropic","unsupported-media","text-plain","url"],"backgroundTag":"unsupported-file-source","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}