# How to Debug Claude Video When No Captions or Transcripts Are Available

> Debug Claude Video without captions or transcripts. Learn to fix missing subtitle tracks, API key issues, and system dependencies for successful transcription with yt-dlp and Whisper API.

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

---

**Claude Video generates transcripts by first attempting to download native YouTube captions via yt-dlp, then falling back to Whisper API transcription (Groq or OpenAI) when none exist, and silent failures typically stem from missing subtitle tracks, unconfigured API keys, or absent system dependencies.**

The `bradautomates/claude-video` repository implements a two-stage transcription pipeline in `skills/watch/scripts/` that automatically processes video sources. When the final analysis report lacks caption data, the failure can be traced to specific bottlenecks in subtitle retrieval, audio extraction, or API authentication. Understanding the exact execution flow through [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py), [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py), and [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py) allows you to diagnose and resolve these issues efficiently.

## Understanding the Two-Stage Transcription Pipeline

Claude Video attempts to build a transcript in two successive stages, with explicit fallback mechanisms between them.

### Stage 1: Native Caption Extraction via yt-dlp

The [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) script invokes **yt-dlp** with `--write-subs --write-auto-subs` to retrieve VTT subtitle files. In `download_url()` (lines 65‑80), the tool requests English subtitles specifically using `--sub-langs en.*` and `--sub-format vtt`. The helper `_pick_subtitle()` (lines 44‑52) then selects the best available VTT file from the download directory. Finally, `parse_vtt()` in [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) (lines 24‑52) parses the subtitle file into a standardized segment format with `start`, `end`, and `text` fields.

### Stage 2: Whisper API Fallback

When native subtitles are unavailable or the source is a local video file, the system falls back to [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py). This script first extracts a mono 16 kHz MP3 audio track using `extract_audio()` (lines 15‑33), which wraps **ffmpeg**. It then uploads the audio to either **Groq** or **OpenAI** Whisper API endpoints. The `transcribe_video()` function (lines 14‑66) orchestrates chunking (respecting `MAX_UPLOAD_BYTES = 24 MiB`), multipart uploads, and error handling. The response is normalized by `_segments_from_response()` into the same segment structure used by the VTT parser.

## Common Failure Points and Diagnostic Symptoms

Missing transcripts usually indicate failures in one of three domains. Use these symptoms to isolate the root cause:

- **yt-dlp subtitle fetch**: The JSON output from [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) shows `"subtitle_path": null` and no VTT files exist in the temporary directory. This occurs when videos lack community captions or automatic subtitles are disabled.

- **Whisper configuration**: The [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py) script raises `SystemExit` regarding missing API keys, or returns `"segments": []`. This happens when `~/.config/watch/.env` lacks `GROQ_API_KEY` or `OPENAI_API_KEY`, or when the environment variables are not loaded.

- **Runtime dependencies**: Scripts abort with "yt-dlp is not installed" or "ffmpeg is not installed" errors. This indicates the pre-flight check failed to locate required binaries.

## Step-by-Step Debugging Workflow

Follow this sequence to isolate where the transcription pipeline breaks.

### 1. Verify System Dependencies

Run the setup check to confirm `ffmpeg`, `ffprobe`, and `yt-dlp` are installed:

```bash
python3 "${SKILL_DIR}/scripts/setup.py" --check

```

A non-zero exit code indicates missing binaries. The installer will print specific installation commands for your platform.

### 2. Inspect the Download Stage

Execute the downloader manually to verify subtitle retrieval:

```bash
python3 "${SKILL_DIR}/scripts/download.py" "https://youtu.be/XYZ" /tmp/watch-demo

```

Examine the printed JSON for `"subtitle_path"`. If the value is `null`:
- Check if the YouTube video actually has CC available in the player
- Re-run with `-v` appended to the yt-dlp arguments to expose HTTP errors or region-blocking issues
- Verify the temporary directory contains `video*.vtt` files

### 3. Test the Whisper Path Directly

Force audio transcription to bypass subtitle retrieval and verify API connectivity:

```bash
python3 "${SKILL_DIR}/scripts/whisper.py" /tmp/watch-demo/video.mp4 --backend groq

```

Watch for extraction messages like "extracting audio for Whisper (groq)…". If the script aborts with key errors, configure your credentials:

```bash
mkdir -p ~/.config/watch
echo "GROQ_API_KEY=your_key_here" >> ~/.config/watch/.env
chmod 600 ~/.config/watch/.env

```

If using OpenAI instead, substitute `OPENAI_API_KEY`.

### 4. Execute the Full Pipeline with Debug Flags

Run the main entry point to see transcript source attribution:

```bash
python3 "${SKILL_DIR}/scripts/watch.py" "https://youtu.be/XYZ" \
    --detail balanced --no-whisper

```

The `--no-whisper` flag isolates whether the issue is strictly missing native captions. If you need to force Whisper (bypassing any available subtitles), omit `--no-whisper` and specify `--whisper openai` or `--whisper groq`.

### 5. Check API Key Precedence

The `load_api_key()` function (in [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py) around lines 65‑30) checks environment variables in this order: `GROQ_API_KEY`, then `OPENAI_API_KEY`. If both are set, Groq takes precedence. Ensure your chosen backend matches the available key.

### 6. Analyze the Final Report Header

The [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) output prints a diagnostic header indicating the transcript source:

```

Transcript source: captions

```

or

```

Transcript source: whisper (groq)

```

If the header reads "none available", the fallback completely failed. Check stderr for HTTP error codes like `401 Unauthorized` (invalid key) or `429 Too Many Requests` (rate limited).

### 7. Handle Rate Limits and Large Files

If Groq returns 429 errors, switch backends or reduce chunk sizes:

```bash
python3 "${SKILL_DIR}/scripts/watch.py" "https://youtu.be/XYZ" --whisper openai

```

For videos exceeding the default chunk size, the split logic automatically handles segmentation, but you can verify upload sizes in the debug output.

### 8. Clean Up Temporary State

After debugging, remove stale working directories to prevent cached state interference:

```bash
rm -rf /tmp/watch-demo

```

## Key Source Files and Implementation Details

Understanding these specific functions helps trace execution:

- **[`skills/watch/scripts/download.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/download.py)**:
  - `download_url()` (lines 65‑80): Executes yt-dlp with `--sub-langs en.*` and `--sub-format vtt`
  - `_pick_subtitle()` (lines 44‑52): Filters and selects optimal VTT files from download results

- **[`skills/watch/scripts/transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/transcribe.py)**:
  - `parse_vtt()` (lines 24‑52): Converts VTT timestamps into normalized segments, handling rolling cue deduplication

- **[`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py)**:
  - `extract_audio()` (lines 15‑33): Runs ffmpeg to generate 16 kHz mono MP3s
  - `transcribe_video()` (lines 14‑66): Manages the complete fallback workflow including chunking and API retries
  - `load_api_key()`: Discovers API keys from `~/.config/watch/.env` or environment variables

- **[`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py)**: The main entry point that coordinates between download, transcription, and output formatting while printing the "Transcript source" diagnostic.

## Summary

- Claude Video prioritizes native **yt-dlp** captions via [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) before falling back to **Whisper** transcription in [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py).
- Debug missing transcripts by checking `subtitle_path` in the download JSON, verifying API keys in `~/.config/watch/.env`, and running `setup.py --check` for dependencies.
- Force specific backends using `--whisper openai` or `--whisper groq`, and isolate caption issues with `--no-whisper`.
- Key functions to instrument: `_pick_subtitle()` for VTT selection, `extract_audio()` for ffmpeg conversion, and `transcribe_video()` for API orchestration.

## Frequently Asked Questions

### Why does Claude Video fail to find captions when YouTube shows "CC" is available?

YouTube's "CC" button indicates automatic captions exist, but yt-dlp may fail to retrieve them if the `--sub-langs` filter excludes the specific language code or if the video owner disabled third-party access to subtitle files. Run [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py) manually with `-v` to see the exact subtitle languages offered by YouTube's API, then adjust the language filter in `download_url()` if necessary.

### How do I force Whisper transcription instead of using native captions?

Pass the `--whisper` flag with your preferred backend when invoking [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py):

```bash
python3 "${SKILL_DIR}/scripts/watch.py" "<url>" --whisper openai

```

This bypasses the VTT parsing in [`transcribe.py`](https://github.com/bradautomates/claude-video/blob/main/transcribe.py) and routes directly to [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py), which extracts audio and calls the API regardless of whether subtitle files exist.

### What causes "401 Unauthorized" or "429 Too Many Requests" errors during transcription?

**401 errors** indicate invalid API keys; verify `GROQ_API_KEY` or `OPENAI_API_KEY` in your `~/.config/watch/.env` file matches the backend specified. **429 errors** indicate rate limiting on Groq's free tier; switch to the OpenAI backend using `--whisper openai` or wait before retrying. The `transcribe_video()` function surfaces these HTTP statuses in stderr when API calls fail.

### Where does Claude Video look for configuration files and API keys?

 According to the source code in [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py), the `load_api_key()` function first checks environment variables (`GROQ_API_KEY`, `OPENAI_API_KEY`), then falls back to `~/.config/watch/.env`. Create this directory and file manually if it does not exist, and ensure permissions are set to `600` to protect your credentials.