{"record":{"id":"b21243b6cdfe1d67","repo":"wavetermdev/waveterm","slug":"file-part-missing-url","errorCode":null,"errorMessage":"file part missing url","messagePattern":"file part missing url","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/anthropic/anthropic-convertmessage.go","lineNumber":357,"sourceCode":"\tfor _, p := range parts {\n\t\tpartBlocks, err := convertPartToAnthropicBlocks(p, role, len(blocks))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"anthropic: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tblocks = append(blocks, partBlocks...)\n\t}\n\n\treturn blocks, nil\n}\n\n// convertFileUIMessagePart converts a file part to Anthropic image or document block format\nfunc convertFileUIMessagePart(p uctypes.UIMessagePart) (*anthropicMessageContentBlock, error) {\n\tif p.Type != \"file\" {\n\t\treturn nil, fmt.Errorf(\"convertFileUIMessagePart expects 'file' type, got '%s'\", p.Type)\n\t}\n\tif p.URL == \"\" {\n\t\treturn nil, errors.New(\"file part missing url\")\n\t}\n\tif p.MediaType == \"\" {\n\t\treturn nil, errors.New(\"file part missing mediaType\")\n\t}\n\n\t// Validate URL protocol - only allow data:, http:, https:\n\tif !strings.HasPrefix(p.URL, \"data:\") &&\n\t\t!strings.HasPrefix(p.URL, \"http://\") &&\n\t\t!strings.HasPrefix(p.URL, \"https://\") {\n\t\treturn nil, fmt.Errorf(\"unsupported URL protocol in file part: %s\", p.URL)\n\t}\n\n\t// Branch on mediaType first to determine block type and constraints\n\tswitch {\n\tcase strings.HasPrefix(p.MediaType, \"image/\"):\n\t\t// image/* (jpeg, png, gif, webp) → Anthropic image block\n\t\tif strings.HasPrefix(p.URL, \"data:\") {\n\t\t\t// Data URL → base64 source","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/anthropic/anthropic-convertmessage.go#L339-L375","documentation":"convertFileUIMessagePart converts a UI message 'file' part into an Anthropic image/document block. A file part with no URL has no source data whatsoever, so conversion is impossible and the function returns 'file part missing url'. Anthropic blocks need either a base64 payload or a fetchable URL.","triggerScenarios":"Sending a chat message whose parts include {Type: \"file\"} with URL == \"\" — e.g. an attachment whose upload never completed, or a client that only set MediaType/FileName.","commonSituations":"Frontend dropping a file before upload finished and still emitting the part; drag-and-drop handling that creates the part before reading the file; copying message-construction code and omitting the URL field.","solutions":["Populate p.URL with a data:, http://, or https:// URL before sending the message.","Filter out file parts with empty URLs prior to calling the conversion/chat step.","Fix the producer so the part is only emitted after the upload/data-url encoding completes."],"exampleFix":"// before\npart := uctypes.UIMessagePart{Type: \"file\", MediaType: \"image/png\"}\n\n// after\ndataURL := \"data:image/png;base64,\" + base64.StdEncoding.EncodeToString(pngBytes)\npart := uctypes.UIMessagePart{Type: \"file\", MediaType: \"image/png\", URL: dataURL}","handlingStrategy":"validation","validationCode":"for _, p := range msg.Parts {\n    if p.Type == \"file\" && p.URL == \"\" {\n        return fmt.Errorf(\"dropping/fixing file part with empty url (mediaType=%s)\", p.MediaType)\n    }\n}","typeGuard":"func hasValidFileURL(p uctypes.UIMessagePart) bool {\n    return p.Type == \"file\" && p.URL != \"\"\n}","tryCatchPattern":"blocks, err := convertPartToAnthropicBlocks(part)\nif err != nil && strings.Contains(err.Error(), \"file part missing url\") {\n    return fmt.Errorf(\"attachment has no source data; re-upload the file: %w\", err)\n}","preventionTips":["Only emit file parts after the upload or data-URL encoding has finished.","Sanitize outgoing messages to drop incomplete attachments.","Validate message parts in the UI layer before submit."],"tags":["validation","message-conversion","attachments"],"backgroundTag":"missing-required-argument","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}