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.pyshows"subtitle_path": nulland no VTT files exist in the temporary directory. This occurs when videos lack community captions or automatic subtitles are disabled. -
Whisper configuration: The
whisper.pyscript raisesSystemExitregarding missing API keys, or returns"segments": []. This happens when~/.config/watch/.envlacksGROQ_API_KEYorOPENAI_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
-vappended to the yt-dlp arguments to expose HTTP errors or region-blocking issues - Verify the temporary directory contains
video*.vttfiles
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 MP3stranscribe_video()(lines 14‑66): Manages the complete fallback workflow including chunking and API retriesload_api_key(): Discovers API keys from~/.config/watch/.envor 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.pybefore falling back to Whisper transcription inwhisper.py. - Debug missing transcripts by checking
subtitle_pathin the download JSON, verifying API keys in~/.config/watch/.env, and runningsetup.py --checkfor dependencies. - Force specific backends using
--whisper openaior--whisper groq, and isolate caption issues with--no-whisper. - Key functions to instrument:
_pick_subtitle()for VTT selection,extract_audio()for ffmpeg conversion, andtranscribe_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →