Billionmail/BillionMail · error

no scenes provided

Error message

no scenes provided

What it means

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.

Source

Thrown at core/internal/service/video_gen/composite.go:185

// Places the lip sync video in the bottom-right corner at 25% of frame size.
func buildPiPFilter(lipSyncIdx int, baseVideo string, cfg CompositeConfig) string {
	pipW := cfg.Width / 4
	pipH := cfg.Height / 4
	pipX := cfg.Width - pipW - 20  // 20px margin
	pipY := cfg.Height - pipH - 20

	return fmt.Sprintf(
		"[%d:v]scale=%d:%d[pip];[%s][pip]overlay=%d:%d:shortest=1[vpip]",
		lipSyncIdx, pipW, pipH, baseVideo, pipX, pipY,
	)
}

// CompositeVideo runs FFmpeg to combine screenshots + audio into a final video.
// Requires: ffmpeg installed and in PATH.
func CompositeVideo(ctx context.Context, cfg CompositeConfig) (*CompositeResult, error) {
	args := BuildFFmpegArgs(cfg)
	if args == nil {
		return nil, fmt.Errorf("no scenes provided")
	}

	// Ensure output directory exists
	if err := os.MkdirAll(filepath.Dir(cfg.OutputPath), 0755); err != nil {
		return nil, fmt.Errorf("create output dir: %w", err)
	}

	cmd := exec.CommandContext(ctx, "ffmpeg", args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("ffmpeg compositing failed: %w\noutput: %s", err, string(out))
	}

	// Calculate total duration from scenes
	var totalDuration time.Duration
	for _, scene := range cfg.Scenes {
		totalDuration += scene.Duration
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Populate cfg.Scenes with at least one scene (screenshot + audio + duration) before calling CompositeVideo
  2. Check the upstream screenshot/annotate steps: if they failed silently, fix that first and re-run
  3. Validate scenes length at the pipeline entry point and fail early with a clearer message
  4. If an empty video is legitimate for your flow, guard the call: skip compositing when len(scenes)==0

Example fix

// before
res, err := video_gen.CompositeVideo(ctx, cfg) // cfg.Scenes may be empty
// after
if len(cfg.Scenes) == 0 {
    return nil, errors.New("cannot composite: no scenes were produced by the screenshot step")
}
res, err := video_gen.CompositeVideo(ctx, cfg)
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Scenes == nil || len(cfg.Scenes) == 0 {
    return errors.New("cannot composite video: no scenes were produced")
}
res, err := video_gen.CompositeVideo(ctx, cfg)

Try / catch

res, err := video_gen.CompositeVideo(ctx, cfg)
if err != nil {
    if err.Error() == "no scenes provided" {
        log.Warn("skipping compositing: empty scene list (check screenshot step)")
        return nil // or surface upstream pipeline failure
    }
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/129ad5f8376be4f0. Report an issue: GitHub.