Billionmail/BillionMail · error
open file for upload: %w
Error message
open file for upload: %w
What it means
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.
Source
Thrown at core/internal/service/video_gen/upload.go:81
URL: R2Endpoint(cfg.AccountID),
}, nil
},
)
return s3.NewFromConfig(aws.Config{
Region: "auto",
Credentials: credentials.NewStaticCredentialsProvider(cfg.AccessKeyID, cfg.AccessKeySecret, ""),
EndpointResolverWithOptions: r2Resolver,
}, func(o *s3.Options) {
o.UsePathStyle = true
})
}
// UploadFile uploads a local file to R2 and returns the public URL.
func UploadFile(ctx context.Context, cfg R2Config, localPath, contactID string) (*UploadResult, error) {
f, err := os.Open(localPath)
if err != nil {
return nil, fmt.Errorf("open file for upload: %w", err)
}
defer f.Close()
filename := filepath.Base(localPath)
key := BuildR2ObjectKey(contactID, filename)
contentType := detectContentType(filename)
client := NewR2Client(cfg)
_, err = client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(cfg.BucketName),
Key: aws.String(key),
Body: f,
ContentType: aws.String(contentType),
})
if err != nil {
return nil, fmt.Errorf("r2 upload: %w", err)
}
View on GitHub (pinned to fc36c76c05)
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
Example fix
// before
f, err := os.Open(localPath)
if err != nil {
return nil, fmt.Errorf("open file for upload: %w", err)
}
// after
if info, statErr := os.Stat(localPath); statErr != nil || info.IsDir() {
return nil, fmt.Errorf("local file missing or not a regular file: %s", localPath)
}
f, err := os.Open(localPath)
if err != nil {
return nil, fmt.Errorf("open file for upload: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
func canUpload(localPath string) error {
info, err := os.Stat(localPath)
if err != nil {
return fmt.Errorf("file missing: %s", localPath)
}
if info.IsDir() {
return fmt.Errorf("not a regular file: %s", localPath)
}
if localPath == "" {
return fmt.Errorf("empty upload path")
}
return nil
} Try / catch
res, err := video_gen.UploadFile(ctx, cfg, localPath, contactID)
if err != nil {
switch {
case errors.Is(err, os.ErrNotExist):
log.Errorf("upload skipped, file missing: %v", err)
case errors.Is(err, fs.ErrPermission):
log.Errorf("upload blocked by permissions: %v", err)
default:
return err
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- file not exists:
- r2 upload: %w
- upload video: %w
- upload thumbnail: %w
- unable to determine domain for noreply email
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/da9b1fe87fc63f48.
Report an issue: GitHub.