{"record":{"id":"129ad5f8376be4f0","repo":"Billionmail/BillionMail","slug":"no-scenes-provided","errorCode":null,"errorMessage":"no scenes provided","messagePattern":"no scenes provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/composite.go","lineNumber":185,"sourceCode":"// Places the lip sync video in the bottom-right corner at 25% of frame size.\nfunc buildPiPFilter(lipSyncIdx int, baseVideo string, cfg CompositeConfig) string {\n\tpipW := cfg.Width / 4\n\tpipH := cfg.Height / 4\n\tpipX := cfg.Width - pipW - 20  // 20px margin\n\tpipY := cfg.Height - pipH - 20\n\n\treturn fmt.Sprintf(\n\t\t\"[%d:v]scale=%d:%d[pip];[%s][pip]overlay=%d:%d:shortest=1[vpip]\",\n\t\tlipSyncIdx, pipW, pipH, baseVideo, pipX, pipY,\n\t)\n}\n\n// CompositeVideo runs FFmpeg to combine screenshots + audio into a final video.\n// Requires: ffmpeg installed and in PATH.\nfunc CompositeVideo(ctx context.Context, cfg CompositeConfig) (*CompositeResult, error) {\n\targs := BuildFFmpegArgs(cfg)\n\tif args == nil {\n\t\treturn nil, fmt.Errorf(\"no scenes provided\")\n\t}\n\n\t// Ensure output directory exists\n\tif err := os.MkdirAll(filepath.Dir(cfg.OutputPath), 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"create output dir: %w\", err)\n\t}\n\n\tcmd := exec.CommandContext(ctx, \"ffmpeg\", args...)\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ffmpeg compositing failed: %w\\noutput: %s\", err, string(out))\n\t}\n\n\t// Calculate total duration from scenes\n\tvar totalDuration time.Duration\n\tfor _, scene := range cfg.Scenes {\n\t\ttotalDuration += scene.Duration\n\t}","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/composite.go#L167-L203","documentation":"CompositeVideo builds the FFmpeg argument list via BuildFFmpegArgs; that builder returns nil when cfg.Scenes is empty/absent. CompositeVideo detects the nil args and aborts with 'no scenes provided' before invoking ffmpeg, because there is nothing to composite. It is a fast-fail validation for an empty composite job.","triggerScenarios":"Calling CompositeVideo with a CompositeConfig whose Scenes slice is nil or has zero entries (e.g. no screenshots were captured upstream, or the scenes slice was reset).","commonSituations":"Upstream screenshot/annotate step produced zero scenes (all screenshots failed); passing an uninitialized struct; a filter dropping all scenes before compositing; wiring bug that never appends to cfg.Scenes.","solutions":["Populate cfg.Scenes with at least one scene (screenshot + audio + duration) before calling CompositeVideo","Check the upstream screenshot/annotate steps: if they failed silently, fix that first and re-run","Validate scenes length at the pipeline entry point and fail early with a clearer message","If an empty video is legitimate for your flow, guard the call: skip compositing when len(scenes)==0"],"exampleFix":"// before\nres, err := video_gen.CompositeVideo(ctx, cfg) // cfg.Scenes may be empty\n// after\nif len(cfg.Scenes) == 0 {\n    return nil, errors.New(\"cannot composite: no scenes were produced by the screenshot step\")\n}\nres, err := video_gen.CompositeVideo(ctx, cfg)","handlingStrategy":"validation","validationCode":"if cfg.Scenes == nil || len(cfg.Scenes) == 0 {\n    return errors.New(\"cannot composite video: no scenes were produced\")\n}\nres, err := video_gen.CompositeVideo(ctx, cfg)","typeGuard":null,"tryCatchPattern":"res, err := video_gen.CompositeVideo(ctx, cfg)\nif err != nil {\n    if err.Error() == \"no scenes provided\" {\n        log.Warn(\"skipping compositing: empty scene list (check screenshot step)\")\n        return nil // or surface upstream pipeline failure\n    }\n    return err\n}","preventionTips":["Validate len(cfg.Scenes) > 0 at the pipeline entry point","Check the screenshot step's logs — an empty scene list usually means it failed silently","Never pass an uninitialized CompositeConfig struct","Decide explicitly how empty output should be handled (skip vs fail) and encode it in the pipeline"],"tags":["ffmpeg","validation","video-gen","empty-input"],"backgroundTag":"empty-input-validation","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"}