{"record":{"id":"da9b1fe87fc63f48","repo":"Billionmail/BillionMail","slug":"open-file-for-upload-w","errorCode":null,"errorMessage":"open file for upload: %w","messagePattern":"open file for upload: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/upload.go","lineNumber":81,"sourceCode":"\t\t\t\tURL: R2Endpoint(cfg.AccountID),\n\t\t\t}, nil\n\t\t},\n\t)\n\n\treturn s3.NewFromConfig(aws.Config{\n\t\tRegion:                      \"auto\",\n\t\tCredentials:                 credentials.NewStaticCredentialsProvider(cfg.AccessKeyID, cfg.AccessKeySecret, \"\"),\n\t\tEndpointResolverWithOptions: r2Resolver,\n\t}, func(o *s3.Options) {\n\t\to.UsePathStyle = true\n\t})\n}\n\n// UploadFile uploads a local file to R2 and returns the public URL.\nfunc UploadFile(ctx context.Context, cfg R2Config, localPath, contactID string) (*UploadResult, error) {\n\tf, err := os.Open(localPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"open file for upload: %w\", err)\n\t}\n\tdefer f.Close()\n\n\tfilename := filepath.Base(localPath)\n\tkey := BuildR2ObjectKey(contactID, filename)\n\tcontentType := detectContentType(filename)\n\n\tclient := NewR2Client(cfg)\n\t_, err = client.PutObject(ctx, &s3.PutObjectInput{\n\t\tBucket:      aws.String(cfg.BucketName),\n\t\tKey:         aws.String(key),\n\t\tBody:        f,\n\t\tContentType: aws.String(contentType),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"r2 upload: %w\", err)\n\t}\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/upload.go#L63-L99","documentation":"UploadFile opens the local file before streaming it to Cloudflare R2, and wraps any os.Open failure with 'open file for upload: %w'. The wrapped error is Go's *PathError (open <path>: no such file or directory / permission denied), preserving the offending path and OS reason.","triggerScenarios":"os.Open(localPath) fails because the file does not exist (TestUploadFile_NonexistentFile, TestIntegration_UploadFile_NonexistentPath_ReturnsWrappedError), localPath is an empty string (TestUploadFile_EmptyPath, yielding 'open : no such file or directory'), the path is a directory, or the process lacks read permission.","commonSituations":"An upstream thumbnail/video generation step failed silently so the file was never written; misconfigured artifact directory or relative vs absolute path confusion; running the worker as a different user without read access; typo'd or empty contact-scoped file paths.","solutions":["os.Stat the localPath first and return a clear 'file not found' message if it is missing, then retry the upload","Verify the generation step that produced the video/thumbnail actually succeeded and wrote to the expected output path","Check file permissions and that the worker process user can read the file","Fix empty/relative path construction in the caller (log the resolved absolute path)","Use errors.Is(err, os.ErrNotExist)/errors.Is(err, fs.ErrPermission) on the unwrapped error to branch precisely"],"exampleFix":"// before\nf, err := os.Open(localPath)\nif err != nil {\n    return nil, fmt.Errorf(\"open file for upload: %w\", err)\n}\n// after\nif info, statErr := os.Stat(localPath); statErr != nil || info.IsDir() {\n    return nil, fmt.Errorf(\"local file missing or not a regular file: %s\", localPath)\n}\nf, err := os.Open(localPath)\nif err != nil {\n    return nil, fmt.Errorf(\"open file for upload: %w\", err)\n}","handlingStrategy":"validation","validationCode":"func canUpload(localPath string) error {\n    info, err := os.Stat(localPath)\n    if err != nil {\n        return fmt.Errorf(\"file missing: %s\", localPath)\n    }\n    if info.IsDir() {\n        return fmt.Errorf(\"not a regular file: %s\", localPath)\n    }\n    if localPath == \"\" {\n        return fmt.Errorf(\"empty upload path\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"res, err := video_gen.UploadFile(ctx, cfg, localPath, contactID)\nif err != nil {\n    switch {\n    case errors.Is(err, os.ErrNotExist):\n        log.Errorf(\"upload skipped, file missing: %v\", err)\n    case errors.Is(err, fs.ErrPermission):\n        log.Errorf(\"upload blocked by permissions: %v\", err)\n    default:\n        return err\n    }\n}","preventionTips":["os.Stat every artifact path before UploadVideoAssets","Surface errors from the generation step instead of ignoring them","Use absolute paths built from a single configured artifact directory","Ensure the worker user has read access to generated files"],"tags":["filesystem","r2","upload","file-not-found"],"backgroundTag":"file-not-found","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}