screenpipe/screenpipe · error · anyhow::Error

export produced an empty file

Error message

export produced an empty file

What it means

After running the ffmpeg mux pipeline, the export routine stats the output MP4 and rejects it if its size is 0 bytes. An empty output means the mux produced no data — usually ffmpeg ran 'successfully' but consumed no valid frames/audio inputs. This is a post-flight sanity check so callers never receive a corrupt/empty file path.

Source

Thrown at crates/screenpipe-engine/src/meeting_export.rs:385

    tokio::fs::write(&concat_path, concat_body)
        .await
        .context("failed to write concat list")?;

    if let Some(parent) = output_path.parent() {
        tokio::fs::create_dir_all(parent)
            .await
            .with_context(|| format!("failed to create output dir {}", parent.display()))?;
    }

    // 8. Single ffmpeg mux: concat (video) + N audio inputs → re-timed video + mixed audio.
    run_mux(&ffmpeg_path, &concat_path, &audio, origin, output_path).await?;

    let file_size_bytes = tokio::fs::metadata(output_path)
        .await
        .map(|m| m.len())
        .unwrap_or(0);
    if file_size_bytes == 0 {
        return Err(anyhow!("export produced an empty file"));
    }

    let summary = MeetingExportSummary {
        output_path: output_path.to_string_lossy().to_string(),
        frame_count: surviving.len(),
        audio_chunk_count: audio.len(),
        duration_secs: timeline_end,
        file_size_bytes,
    };
    info!(
        "export complete: {} frames, {} audio chunks, {:.1}s, {} bytes -> {}",
        summary.frame_count,
        summary.audio_chunk_count,
        summary.duration_secs,
        summary.file_size_bytes,
        summary.output_path
    );
    Ok(summary)

View on GitHub (pinned to 4ebf712990)

Solutions

  1. Check that surviving frames reference existing, non-empty source video files before calling export (disk existence + size > 0).
  2. Widen the requested time range or reduce filtering so at least one frame survives export.
  3. Verify free disk space and write permissions on output_path.
  4. Run the export again with ffmpeg stderr logging enabled to see warnings from the mux steps.

Example fix

// before
let summary = export_range_to_mp4(&state, range, out_path).await?;
// after
if !range_has_surviving_frames(&state, &range).await? {
    return Err(anyhow!("no frames in the requested range; nothing to export"));
}
let summary = export_range_to_mp4(&state, range, out_path).await?;
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn validate_export_inputs(frames: &[FrameRef]) -> Result<(), String> {
    if frames.is_empty() { return Err("no frames in range".into()); }
    for f in frames {
        let p = Path::new(&f.video_path);
        match std::fs::metadata(p) {
            Ok(m) if m.len() > 0 => {}
            Ok(_) => return Err(format!("empty source video: {}", f.video_path)),
            Err(_) => return Err(format!("missing source video: {}", f.video_path)),
        }
    }
    Ok(())
}

Prevention

When it happens

Trigger: Calling export_range_to_mp4 or export_range_to_mp4_video_only when the selected frame range has no surviving frames, all source video files referenced by the frames are missing or truncated (0-byte), or ffmpeg's concat/mux steps produced an output file that was never written (e.g. disk full or output path unwritable so metadata.len() is 0).

Common situations: User asks to export a meeting window that was pruned to zero frames after dedup/snapshot filtering; video files were deleted by retention/cleanup before export; output disk ran out of space mid-write; output path points to a directory or locked file so ffmpeg wrote nothing.

Related errors


AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01). Data as JSON: /api/errors/e5f21cf6e565a0a2. Report an issue: GitHub.