{"record":{"id":"637b5d1adf9afc36","repo":"Billionmail/BillionMail","slug":"upload-video-w","errorCode":null,"errorMessage":"upload video: %w","messagePattern":"upload video: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/upload.go","lineNumber":111,"sourceCode":"\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\n\treturn &UploadResult{\n\t\tKey:       key,\n\t\tPublicURL: BuildPublicURL(cfg, key),\n\t}, nil\n}\n\n// UploadVideoAssets uploads the video and thumbnail to R2.\n// Returns public URLs for both.\nfunc UploadVideoAssets(ctx context.Context, cfg R2Config, videoPath, thumbnailPath, contactID string) (videoURL, thumbURL string, err error) {\n\tvideoResult, err := UploadFile(ctx, cfg, videoPath, contactID)\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"upload video: %w\", err)\n\t}\n\n\tthumbResult, err := UploadFile(ctx, cfg, thumbnailPath, contactID)\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"upload thumbnail: %w\", err)\n\t}\n\n\treturn videoResult.PublicURL, thumbResult.PublicURL, nil\n}\n\n// detectContentType returns the MIME type based on file extension.\nfunc detectContentType(filename string) string {\n\text := strings.ToLower(filepath.Ext(filename))\n\tswitch ext {\n\tcase \".mp4\":\n\t\treturn \"video/mp4\"\n\tcase \".webm\":\n\t\treturn \"video/webm\"","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/upload.go#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvideoResult, err := UploadFile(ctx, cfg, videoPath, contactID)\nif err != nil {\n    return \"\", \"\", fmt.Errorf(\"upload video: %w\", err)\n}\n// after\nif _, err := os.Stat(videoPath); err != nil {\n    return \"\", \"\", fmt.Errorf(\"video file missing before upload: %s\", videoPath)\n}\nvideoResult, err := UploadFile(ctx, cfg, videoPath, contactID)\nif err != nil {\n    return \"\", \"\", fmt.Errorf(\"upload video: %w\", err)\n}","handlingStrategy":"validation","validationCode":"for _, p := range []string{videoPath, thumbnailPath} {\n    if _, err := os.Stat(p); err != nil {\n        return fmt.Errorf(\"asset missing before upload: %s\", p)\n    }\n}\nif err := validateR2Config(cfg); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"vURL, tURL, err := video_gen.UploadVideoAssets(ctx, cfg, videoPath, thumbPath, contactID)\nif err != nil {\n    var pathErr *os.PathError\n    if errors.As(err, &pathErr) {\n        log.Errorf(\"upload stage failed on local file: %v\", err) // chain shows which asset\n    } else {\n        return fmt.Errorf(\"pipeline upload stage: %w\", err)\n    }\n}","preventionTips":["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"],"tags":["r2","upload","pipeline","error-wrapping"],"backgroundTag":"error-wrapping-chain","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}