Billionmail/BillionMail · error
upload video: %w
Error message
upload video: %w
What it means
UploadVideoAssets uploads the video then the thumbnail via UploadFile and wraps the first failure with 'upload video: %w', so callers get a labeled pipeline-stage error whose chain still contains the underlying 'open file for upload' or 'r2 upload' cause.
Source
Thrown at core/internal/service/video_gen/upload.go:111
Body: f,
ContentType: aws.String(contentType),
})
if err != nil {
return nil, fmt.Errorf("r2 upload: %w", err)
}
return &UploadResult{
Key: key,
PublicURL: BuildPublicURL(cfg, key),
}, nil
}
// UploadVideoAssets uploads the video and thumbnail to R2.
// Returns public URLs for both.
func UploadVideoAssets(ctx context.Context, cfg R2Config, videoPath, thumbnailPath, contactID string) (videoURL, thumbURL string, err error) {
videoResult, err := UploadFile(ctx, cfg, videoPath, contactID)
if err != nil {
return "", "", fmt.Errorf("upload video: %w", err)
}
thumbResult, err := UploadFile(ctx, cfg, thumbnailPath, contactID)
if err != nil {
return "", "", fmt.Errorf("upload thumbnail: %w", err)
}
return videoResult.PublicURL, thumbResult.PublicURL, nil
}
// detectContentType returns the MIME type based on file extension.
func detectContentType(filename string) string {
ext := strings.ToLower(filepath.Ext(filename))
switch ext {
case ".mp4":
return "video/mp4"
case ".webm":
return "video/webm"View on GitHub (pinned to fc36c76c05)
Solutions
- Inspect errors.Is/As down the wrapped chain to find whether it is a file-open failure (fix the video path/generation) or an R2 failure (fix config/network)
- Ensure RunPipeline's video generation step succeeds and cfg.OutputPath matches the videoPath passed here before uploading
- Validate both videoPath and thumbnailPath with os.Stat before calling UploadVideoAssets
- Verify R2Config credentials/endpoint if uploads fail uniformly
- Use errors.Is(err, context.Canceled) to distinguish intentional shutdowns from real failures
Example fix
// before
videoResult, err := UploadFile(ctx, cfg, videoPath, contactID)
if err != nil {
return "", "", fmt.Errorf("upload video: %w", err)
}
// after
if _, err := os.Stat(videoPath); err != nil {
return "", "", fmt.Errorf("video file missing before upload: %s", videoPath)
}
videoResult, err := UploadFile(ctx, cfg, videoPath, contactID)
if err != nil {
return "", "", fmt.Errorf("upload video: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
for _, p := range []string{videoPath, thumbnailPath} {
if _, err := os.Stat(p); err != nil {
return fmt.Errorf("asset missing before upload: %s", p)
}
}
if err := validateR2Config(cfg); err != nil {
return err
} Try / catch
vURL, tURL, err := video_gen.UploadVideoAssets(ctx, cfg, videoPath, thumbPath, contactID)
if err != nil {
var pathErr *os.PathError
if errors.As(err, &pathErr) {
log.Errorf("upload stage failed on local file: %v", err) // chain shows which asset
} else {
return fmt.Errorf("pipeline upload stage: %w", err)
}
} Prevention
- Check the wrapped chain (errors.As) to identify file-open vs r2-upload causes
- Make the video generation step fail loudly so uploads are never attempted on missing artifacts
- Log the asset paths alongside the wrapped error for triage
- Share one validated R2Config across the pipeline
When it happens
Trigger: UploadFile(ctx, cfg, videoPath, contactID) fails because the generated video file is missing on disk (TestUploadVideoAssets_FirstFileMissing) or the R2 PutObject call errors; the wrapped chain is then labeled as the video-upload stage.
Common situations: Video generation step failed or wrote to a different output path so the file never exists; R2 credentials/endpoint misconfigured for every upload; worker context cancelled during a long video upload.
Related errors
- upload thumbnail: %w
- open file for upload: %w
- r2 upload: %w
- error getting site map: %v
- error getting footer config: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/637b5d1adf9afc36.
Report an issue: GitHub.