{"record":{"id":"5c9f1dba11b25e62","repo":"wavetermdev/waveterm","slug":"file-part-missing-both-url-and-data","errorCode":null,"errorMessage":"file part missing both url and data","messagePattern":"file part missing both url and data","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/aiutil/aiutil.go","lineNumber":82,"sourceCode":"\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)\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\")","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/aiutil/aiutil.go#L64-L100","documentation":"ExtractImageUrl builds the image URL for a file message part. It throws this error when a file part has neither a usable URL nor any raw bytes, so there is no way to construct an image reference for the model. This guards the AI message conversion path from emitting empty/dead image parts.","triggerScenarios":"Calling ExtractImageUrl (via convertFileAIMessagePart/convertAIMessageMultimodal) with a UIMessageDataUserFile-style part where url == \"\" and len(data) == 0. Note: if url is set but uses an unsupported protocol, a different error (unsupported URL protocol) is returned instead.","commonSituations":"Constructing AI message parts programmatically and forgetting to read the file bytes into Data; a previously-attached file whose URL was revoked or never populated; serialization round-trips that drop the Data field.","solutions":["Populate either the URL field (http/https/data:) or the raw Data bytes before building the message part.","Fetch the file content and pass its bytes as data (it will be base64-encoded into a data: URL automatically).","If the part is unrecoverable, drop the file part from the message instead of passing an empty one.","Wrap the call in error handling and skip/replace the part with a text note explaining the attachment failed."],"exampleFix":"// before\npart := &uctypes.UIMessagePart{Type: \"file\", Data: uctypes.UIMessageDataUserFile{FileName: \"img.png\", MimeType: \"image/png\"}}\nurl, err := aiutil.ExtractImageUrl(nil, \"\", \"image/png\") // error\n// after\nfileData, _ := os.ReadFile(\"img.png\")\nurl, err := aiutil.ExtractImageUrl(fileData, \"\", \"image/png\") // data:image/png;base64,...","handlingStrategy":"validation","validationCode":"func validImagePart(data []byte, url string) bool {\n    if len(data) > 0 { return true }\n    return url != \"\" && (strings.HasPrefix(url, \"data:\") || strings.HasPrefix(url, \"http://\") || strings.HasPrefix(url, \"https://\"))\n}\n// call ExtractImageUrl only if validImagePart(data, url)","typeGuard":"func hasImageSource(data []byte, url string) bool { return len(data) > 0 || url != \"\" }","tryCatchPattern":"url, err := aiutil.ExtractImageUrl(data, url, mime)\nif err != nil {\n    log.Printf(\"skipping image part: %v\", err)\n    return nil // drop or substitute part\n}","preventionTips":["Always read file bytes into Data before constructing file parts unless you have a valid http(s)/data URL","Add a constructor helper that requires at least one of url/data at creation time","Unit-test message-building code with parts missing each field"],"tags":["multimodal","file-attachment","input-validation"],"backgroundTag":"missing-file-content","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}