Failure Modes for Video Downloads in bradautomates/claude-video: Complete Guide
Video downloads in bradautomates/claude-video fail primarily due to missing yt-dlp dependencies, region-locked content triggering non-zero exits, filesystem permission errors, and unsupported input formats, with most failures surfacing as SystemExit exceptions in skills/watch/scripts/download.py.
The bradautomates/claude-video repository provides an AI-powered video processing pipeline that delegates remote fetching to yt-dlp while handling local file resolution. Understanding the specific failure modes—from dependency misconfiguration to YouTube region restrictions—enables developers to implement defensive error handling and diagnose production issues.
Environment and Dependency Failures
The download pipeline requires the yt-dlp binary to be available in the system PATH. Both the download_url and fetch_captions functions begin with a strict dependency check using shutil.which("yt-dlp"). When this returns None, the code raises an immediate SystemExit with an installation hint at lines 20-22:
# skills/watch/scripts/download.py (lines 20-22)
if shutil.which("yt-dlp") is None:
raise SystemExit("yt-dlp is not installed...")
Filesystem permission errors represent an uncaught failure mode. When the output directory specified in out_dir.mkdir(parents=True, exist_ok=True) (invoked in both fetch_captions and download_url) resides on a read-only mount or lacks write permissions, the code propagates a raw PermissionError rather than handling it gracefully, causing the script to terminate with a traceback.
Input Validation and Local File Errors
When processing local video paths, the resolve_local function validates file existence and extension compatibility.
If a user provides a path that does not exist, p.exists() returns False, triggering a SystemExit at lines 29-31:
# skills/watch/scripts/download.py (lines 29-31)
if not p.exists():
raise SystemExit(f"File not found: {source}")
For unsupported video extensions, the code checks against a VIDEO_EXTS whitelist at lines 33-35. While this emits a warning, execution continues, potentially resulting in downstream processing errors if the container format is actually invalid.
A subtle validation trap occurs when input strings start with a dash (-). The is_url function (lines 21-22) deliberately returns False for these inputs to prevent option injection, forcing local path resolution that typically fails with "File not found".
External Service Failures and yt-dlp Errors
Network-level problems and platform restrictions manifest through yt-dlp's subprocess return codes. The code executes yt-dlp via subprocess.run, then validates success by checking for video file production via _pick_video.
Region-locked or age-restricted videos cause yt-dlp to exit with a non-zero status without producing a video file. The detection logic at lines 49-53 raises SystemExit containing the specific exit code:
# skills/watch/scripts/download.py (lines 49-53)
video_path = _pick_video(out_dir)
if video_path is None:
raise SystemExit(f"yt-dlp failed with exit code {result.returncode}")
However, the code implements best-effort handling for partial failures. As noted in the comment at lines 45-47, some yt-dlp failures—such as 429 rate-limits on subtitle tracks—still generate a valid video file. When _pick_video locates a video despite a non-zero exit code, the script treats the download as successful and proceeds.
Post-Download Validation Issues
Even when yt-dlp completes, downstream components face validation failures.
Missing subtitle tracks occur when videos lack English caption patterns matching en.*. The _pick_subtitle function (lines 46-48) returns None when the candidates list is empty, causing transcribe.py to fall back to Whisper transcription if enabled.
Corrupted metadata presents another failure vector. The _read_info function attempts to parse info.json written by yt-dlp. If parsing fails due to malformed JSON or missing files (lines 99-111), the function catches all exceptions and returns a minimal fallback dictionary:
# skills/watch/scripts/download.py (lines 99-111)
def _read_info(out_dir: Path, url: str):
try:
# ... JSON parsing logic ...
except Exception:
logger.error("Failed to parse info.json")
return {"url": url}
Filesystem-full errors during download can write partial video files that pass the existence check but contain corrupted data. The current implementation does not verify file integrity via size checks or ffprobe validation before returning success.
Handling Failures in Practice
When integrating the download module, wrap calls to capture SystemExit exceptions that contain user-friendly error messages:
from pathlib import Path
from skills.watch.scripts import download
try:
result = download.download(
"https://www.youtube.com/watch?v=example",
Path("/tmp/claude-video"),
audio_only=False,
)
print("Video saved at:", result["video_path"])
if result["subtitle_path"]:
print("Subtitle file:", result["subtitle_path"])
except SystemExit as e:
# Catches missing yt-dlp, region-lock failures, and missing local files
print("Download failed:", e)
This pattern catches environment errors, region-lock failures, and missing local files while allowing successful downloads with optional subtitle absence to proceed.
Summary
- Missing dependencies:
yt-dlpabsence triggers immediateSystemExitat lines 20-22 before any network operations begin. - Region-locked content: Causes non-zero exit codes with no video output, detected via
_pick_videoreturningNoneat lines 49-53. - Local file errors: Nonexistent paths raise
SystemExitat lines 29-31; unsupported extensions trigger warnings only at lines 33-35. - Permission failures: Uncaught
PermissionErrorduringout_dir.mkdircalls requires manual write access verification. - Partial failures: Best-effort logic accepts video files despite subtitle download failures or non-zero exit codes (lines 45-47).
- Metadata corruption:
_read_infofalls back to minimal URL-only dictionaries wheninfo.jsonparsing fails at lines 99-111.
Frequently Asked Questions
What causes "yt-dlp is not installed" errors in claude-video?
The error occurs when shutil.which("yt-dlp") returns None at the start of download_url or fetch_captions functions. This check at lines 20-22 ensures the binary exists in the system PATH before attempting subprocess calls, raising SystemExit with installation instructions if the dependency is missing.
How does claude-video handle region-locked YouTube videos?
Region-locked videos cause yt-dlp to exit with a non-zero status code without writing a video file. The _pick_video function returns None in this scenario, triggering SystemExit at lines 49-53 with the specific exit code. Unlike partial subtitle failures, region locks do not produce best-effort output because no video file is generated for the picker to locate.
Why does my download succeed but subtitles return None?
When videos lack English caption tracks matching the en.* pattern, the _pick_subtitle function (lines 46-48) returns None after finding no candidates in the downloaded files. This is expected behavior for videos without English captions, causing downstream transcribe.py to optionally fall back to Whisper transcription rather than raising an error.
What happens when yt-dlp returns a non-zero exit code but a video file exists?
The code implements best-effort error handling per the comment at lines 45-47. If _pick_video locates a valid video file in the output directory despite a non-zero exit code (common with subtitle track 429 errors), the script treats the download as successful and proceeds with processing, though subtitle_path may be None due to the accompanying subtitle fetch failure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →