{"record":{"id":"b00085c6afa9f438","repo":"Billionmail/BillionMail","slug":"upload-thumbnail-w","errorCode":null,"errorMessage":"upload thumbnail: %w","messagePattern":"upload thumbnail: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/upload.go","lineNumber":116,"sourceCode":"\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\"\n\tcase \".png\":\n\t\treturn \"image/png\"\n\tcase \".jpg\", \".jpeg\":\n\t\treturn \"image/jpeg\"\n\tcase \".gif\":","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/upload.go#L98-L134","documentation":"The thumbnail stage of UploadVideoAssets wraps UploadFile failures for thumbnailPath with 'upload thumbnail: %w', mirroring the video-stage wrapper so logs identify which asset failed. The full underlying cause (file open or R2 error) remains reachable via errors.Unwrap/errors.As.","triggerScenarios":"UploadFile(ctx, cfg, thumbnailPath, contactID) fails because the thumbnail file does not exist (TestUploadVideoAssets_SecondFileMissing — typically because GenerateThumbnail failed earlier) or the R2 PutObject for the thumbnail errors.","commonSituations":"GenerateThumbnail returned an error that was logged-and-ignored upstream, leaving no thumbnail on disk; wrong thumbnail output path configured; R2 issues that also would have failed the video upload; context cancelled between the two uploads.","solutions":["Check the wrapped chain: if it is 'open file for upload', the thumbnail was never generated — rerun GenerateThumbnail and surface its error instead of ignoring it","Verify the thumbnailPath matches the OutputPath returned by GenerateThumbnail","os.Stat thumbnailPath before the upload stage and fail fast with a clear message","If R2-side, apply the same endpoint/credential/bucket checks as the 'r2 upload' error","Compare with the video-stage result: if video succeeded, the R2 config is fine and the problem is local to the thumbnail file"],"exampleFix":"// before\nthumbResult, err := UploadFile(ctx, cfg, thumbnailPath, contactID)\nif err != nil {\n    return \"\", \"\", fmt.Errorf(\"upload thumbnail: %w\", err)\n}\n// after\nif _, err := os.Stat(thumbnailPath); err != nil {\n    return \"\", \"\", fmt.Errorf(\"thumbnail file missing (was GenerateThumbnail run?): %s\", thumbnailPath)\n}\nthumbResult, err := UploadFile(ctx, cfg, thumbnailPath, contactID)\nif err != nil {\n    return \"\", \"\", fmt.Errorf(\"upload thumbnail: %w\", err)\n}","handlingStrategy":"validation","validationCode":"if thumbPath == \"\" {\n    return fmt.Errorf(\"no thumbnail: GenerateThumbnail likely failed upstream\")\n}\nif _, err := os.Stat(thumbPath); err != nil {\n    return fmt.Errorf(\"thumbnail missing at %s\", thumbPath)\n}","typeGuard":null,"tryCatchPattern":"vURL, tURL, err := video_gen.UploadVideoAssets(ctx, cfg, videoPath, thumbPath, contactID)\nif err != nil {\n    if strings.Contains(fmt.Sprint(err), \"upload thumbnail\") || errors.Is(err, os.ErrNotExist) {\n        log.Errorf(\"thumbnail stage failed: %v\", err)\n        // re-run GenerateThumbnail before retrying the upload\n    }\n}","preventionTips":["Always propagate GenerateThumbnail errors; never log-and-continue into the upload stage","Set thumbnailPath to exactly the OutputPath GenerateThumbnail returned","Stat the thumbnail right before UploadVideoAssets","Add a pipeline integration test for the thumbnail-missing case (mirrors TestUploadVideoAssets_SecondFileMissing)"],"tags":["r2","upload","thumbnail","pipeline"],"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-14T00:17:10.932Z"}