{"record":{"id":"e9763b66ef8b1f50","repo":"screenpipe/screenpipe","slug":"video-corrupted-file-too-small-bytes","errorCode":null,"errorMessage":"VIDEO_CORRUPTED: file too small ({} bytes) {}","messagePattern":"VIDEO_CORRUPTED: file too small \\((.+?) bytes\\) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/screenpipe-engine/src/video_utils.rs","lineNumber":1122,"sourceCode":"\npub async fn extract_frame_from_video(\n    file_path: &str,\n    offset_index: i64,\n    jpeg_quality: &str,\n) -> Result<String> {\n    if offset_index < 0 {\n        return Err(anyhow::anyhow!(\n            \"invalid negative frame index: {}\",\n            offset_index\n        ));\n    }\n    let metadata = ensure_regular_media_file(file_path).await?;\n    if metadata.len() == 0 {\n        return Err(anyhow::anyhow!(\"VIDEO_CORRUPTED: empty file {}\", file_path));\n    }\n    // Files under 1KB are likely corrupted (no valid video that small).\n    if metadata.len() < 1024 {\n        return Err(anyhow::anyhow!(\n            \"VIDEO_CORRUPTED: file too small ({} bytes) {}\",\n            metadata.len(),\n            file_path\n        ));\n    }\n\n    let ffmpeg_path =\n        find_ffmpeg_path().ok_or_else(|| anyhow::anyhow!(\"failed to find ffmpeg path\"))?;\n\n    // A file whose metadata will not parse is still worth one extraction\n    // attempt, but if that attempt also fails the file is unreadable rather\n    // than merely awkward — the route maps that to 410 instead of 500.\n    let metadata = probe_frame_metadata(&ffmpeg_path, file_path).await?;\n    let metadata_unreadable = metadata.is_none();\n    let locator = plan_frame_locator(file_path, offset_index, metadata);\n\n    // Create a temporary directory for frames if it doesn't exist\n    let frames_dir = PathBuf::from(\"/tmp/screenpipe_frames\");","sourceCodeStart":1104,"sourceCodeEnd":1140,"githubUrl":"https://github.com/screenpipe/screenpipe/blob/4ebf712990fee17eeaf904dacf749b6e96ac9bf3/crates/screenpipe-engine/src/video_utils.rs#L1104-L1140","documentation":"Files smaller than 1024 bytes cannot hold a valid video container (no moov/ftyp atoms of substance), so the function treats them as corrupted before wasting an ffmpeg spawn, using the same VIDEO_CORRUPTED prefix mapped to 410 by the HTTP route.","triggerScenarios":"extract_frame_from_video (or its callers generate_thumbnail / try_extract_and_serve_frame / run_frame_ocr) is given a regular file whose metadata.len() is between 1 and 1023 bytes.","commonSituations":"Same truncation causes as the empty-file case but partially written: crash after writing only the header, interrupted flush, tiny placeholder files from a sync client.","solutions":["Delete/quarantine the undersized file; it is unrecoverable as video","Regenerate the segment by re-recording","Investigate the writer path (disk space, abrupt shutdown) if many such files appear","Sweep the media directory at startup for files < 1KB"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn looks_like_video(path: &str) -> bool {\n    std::fs::metadata(path).map(|m| m.len() >= 1024).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match extract_frame_from_video(&path, idx, \"2\").await {\n    Ok(jpeg) => jpeg,\n    Err(e) if e.to_string().starts_with(\"VIDEO_CORRUPTED\") => None, // mark file dead\n    Err(e) => return Err(e),\n}","preventionTips":["Treat any media file < 1KB as garbage and prune it proactively","Check write completion (fsync/close) before exposing segments to extraction","Monitor truncation causes: power loss, OOM kills, disk pressure","Regenerate affected segments rather than retrying extraction"],"tags":["video","corruption","filesystem"],"backgroundTag":"video-corrupted","analyzedSha":"4ebf712990fee17eeaf904dacf749b6e96ac9bf3","analyzedAt":"2026-09-01T23:33:43.065Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}