{"record":{"id":"bc39ad698b672881","repo":"Tencent/WeKnora","slug":"failed-to-open-file-w-bc39ad","errorCode":null,"errorMessage":"failed to open file: %w","messagePattern":"failed to open file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/oss.go","lineNumber":174,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check OSS bucket: %w\", err)\n\t}\n\tif !exists {\n\t\treturn fmt.Errorf(\"bucket %q does not exist\", s.bucketName)\n\t}\n\treturn nil\n}\n\n// SaveFile saves a file to OSS using the Uploader manager for large files.\nfunc (s *ossFileService) SaveFile(ctx context.Context,\n\tfile *multipart.FileHeader, tenantID uint64, knowledgeID string,\n) (string, error) {\n\text := filepath.Ext(file.Filename)\n\tobjectName := fmt.Sprintf(\"%s%d/%s/%s%s\", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)\n\n\tsrc, err := file.Open()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to open file: %w\", err)\n\t}\n\tdefer src.Close()\n\n\tcontentType := file.Header.Get(\"Content-Type\")\n\tif contentType == \"\" {\n\t\tcontentType = utils.GetContentTypeByExt(ext)\n\t}\n\n\t// Use Uploader for files > 10MB (auto multipart with concurrent uploads)\n\tconst multipartThreshold = 10 * 1024 * 1024\n\tif file.Size > multipartThreshold {\n\t\tuploader := s.client.NewUploader(func(uo *oss.UploaderOptions) {\n\t\t\tuo.PartSize = 10 * 1024 * 1024 // 10MB per part\n\t\t\tuo.ParallelNum = 3             // 3 concurrent uploads\n\t\t})\n\n\t\t_, err = uploader.UploadFrom(ctx,\n\t\t\t&oss.PutObjectRequest{","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/oss.go#L156-L192","documentation":"SaveFile opens the uploaded multipart file (file.Open() on a *multipart.FileHeader) before uploading to OSS; this error wraps any failure from that open operation with 'failed to open file: %w'. It means the multipart part backing the upload could not be read — typically because the underlying temp file or request body is no longer available.","triggerScenarios":"Calling SaveFile with a *multipart.FileHeader whose underlying temp file was removed, whose request body was already consumed/closed, or a corrupted multipart form; also possible when the FileHeader was retained past the HTTP request lifetime (Go cleans request temp files when the handler returns).","commonSituations":"Storing multipart.FileHeader in a queue/cache and calling SaveFile after the HTTP request has ended; server temp directory (TMPDIR) permissions or disk-full preventing temp file writes; client disconnected mid-upload; middleware closing the request body early.","solutions":["Upload synchronously inside the request handler before returning — never defer SaveFile to a goroutine or background job using the same FileHeader","Verify the server temp dir exists, is writable, and has free space (check TMPDIR, disk usage, permissions)","Read the multipart form fully and check r.ParseMultipartForm errors before accessing the FileHeader","If async processing is needed, copy the file contents into a buffer/temp file you own within the handler, then pass that"],"exampleFix":"// before (broken async)\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    f, _, _ := r.FormFile(\"file\")\n    go svc.SaveFile(ctx, tenantID, kid, f) // temp file gone by the time goroutine runs\n}\n// after\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    fh, _, err := r.FormFile(\"file\")\n    if err != nil { http.Error(w, err.Error(), 400); return }\n    defer fh.Close()\n    if _, err := svc.SaveFile(r.Context(), tenantID, kid, fh); err != nil {\n        http.Error(w, err.Error(), 500); return\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Before SaveFile, verify the FileHeader is readable in the same request lifecycle\nf, err := fh.Open()\nif err != nil { return fmt.Errorf(\"upload unreadable: %w\", err) }\nf.Close()\n// Also check temp space\nif st, err := os.Stat(os.TempDir()); err != nil || !st.IsDir() { return errors.New(\"temp dir unavailable\") }","typeGuard":null,"tryCatchPattern":"objectPath, err := svc.SaveFile(ctx, tenantID, knowledgeID, fileHeader)\nif err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        return fmt.Errorf(\"upload temp file inaccessible (%s): %w\", pe.Path, err)\n    }\n    return fmt.Errorf(\"save failed: %w\", err)\n}","preventionTips":["Always call SaveFile synchronously within the HTTP request handler; copy to your own buffer first if async processing is required","Monitor server temp-dir writability and free disk space","Handle client-disconnect and ParseMultipartForm errors before touching the FileHeader","Never reuse multipart.FileHeader objects across requests or after the request context ends"],"tags":["oss","file-upload","multipart","io"],"backgroundTag":"multipart-file-open-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}