# Failure Modes for Video Downloads in bradautomates/claude-video: Complete Guide

> Explore common failure modes for video downloads in bradautomates/claude-video including missing dependencies, region locks, and permissions. Fix your downloads now.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: how-to-guide
- Published: 2026-07-30

---

**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`](https://github.com/bradautomates/claude-video/blob/main/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:

```python

# 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:

```python

# 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:

```python

# 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`](https://github.com/bradautomates/claude-video/blob/main/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`](https://github.com/bradautomates/claude-video/blob/main/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:

```python

# 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:

```python
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-dlp` absence triggers immediate `SystemExit` at lines 20-22 before any network operations begin.
- **Region-locked content**: Causes non-zero exit codes with no video output, detected via `_pick_video` returning `None` at lines 49-53.
- **Local file errors**: Nonexistent paths raise `SystemExit` at lines 29-31; unsupported extensions trigger warnings only at lines 33-35.
- **Permission failures**: Uncaught `PermissionError` during `out_dir.mkdir` calls 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_info` falls back to minimal URL-only dictionaries when [`info.json`](https://github.com/bradautomates/claude-video/blob/main/info.json) parsing 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`](https://github.com/bradautomates/claude-video/blob/main/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.