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

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, transcribe.py, and 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 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 (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. 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 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 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:

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:

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:

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:

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:

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

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:

rm -rf /tmp/watch-demo

Key Source Files and Implementation Details

Understanding these specific functions helps trace execution:

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

    • parse_vtt() (lines 24‑52): Converts VTT timestamps into normalized segments, handling rolling cue deduplication
  • 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: 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 before falling back to Whisper transcription in 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 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:

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

This bypasses the VTT parsing in transcribe.py and routes directly to 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, 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →