crewAIInc/crewAI · error · ValueError

Unable to extract transcript from YouTube video {video_id}:

Error message

Unable to extract transcript from YouTube video {video_id}: {e!s}

What it means

The video loader's outer catch-all ValueError wrapping every exception during transcript retrieval and metadata enrichment. Because it uses 'except Exception', it also swallows and re-wraps the loader's own 'No transcript available' raise (254) — check the message suffix or __cause__ to distinguish missing captions from genuine failures. Inner metadata enrichment failures (title/author via pytube) are deliberately ignored with a bare pass, so this error almost always comes from the transcript API call itself.

Source

Thrown at lib/crewai-tools/src/crewai_tools/rag/loaders/youtube_video_loader.py:94

                    yt = YouTube(video_url)
                    metadata["title"] = yt.title
                    metadata["author"] = yt.author
                    metadata["length_seconds"] = yt.length
                    metadata["description"] = (
                        yt.description[:500] if yt.description else None
                    )

                    if yt.title:
                        content = f"Title: {yt.title}\n\nAuthor: {yt.author or 'Unknown'}\n\nTranscript:\n{content}"
                except Exception:  # noqa: S110
                    pass
            else:
                raise ValueError(
                    f"No transcript available for YouTube video: {video_id}"
                )

        except Exception as e:
            raise ValueError(
                f"Unable to extract transcript from YouTube video {video_id}: {e!s}"
            ) from e

        return LoaderResult(
            content=content,
            source=video_url,
            metadata=metadata,
            doc_id=self.generate_doc_id(source_ref=video_url, content=content),
        )

    @staticmethod
    def _extract_video_id(url: str) -> str | None:
        """Extract video ID from various YouTube URL formats."""
        patterns = [
            r"(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/)([^&\n?#]+)",
        ]

        for pattern in patterns:

View on GitHub (pinned to 754d7323be)

Solutions

  1. Inspect e.__cause__ / the message suffix to classify: 'No transcript available' = content issue (see 254); 'blocked' / RequestBlocked = IP rate-limit; other = network or version issue.
  2. Upgrade or pin youtube-transcript-api to a version compatible with the loader's usage: uv add 'youtube-transcript-api>=1.0'.
  3. If blocked, route requests through a proxy the transcript API supports, or reduce request frequency / add caching.
  4. Add retry with backoff for transient network errors before surfacing the failure.

Example fix

# before
try:
    result = loader.load(src)
except ValueError as e:
    raise  # loses classification

# after
try:
    result = loader.load(src)
except ValueError as e:
    msg = str(e)
    if "No transcript available" in msg:
        skip(src)
    elif "blocked" in msg.lower():
        backoff_and_retry(src)
    else:
        raise
Defensive patterns

Strategy: retry

Validate before calling

def classify_yt_failure(exc: ValueError) -> str:
    msg = str(exc)
    if "No transcript available" in msg:
        return "no-captions"
    if "blocked" in msg.lower() or "ip" in msg.lower():
        return "rate-limited"
    return "unknown"

Try / catch

attempt = 0
while attempt < 3:
    try:
        result = yt_video_loader.load(src)
        break
    except ValueError as e:
        kind = classify_yt_failure(e)
        if kind == "rate-limited" and attempt < 2:
            attempt += 1
            time.sleep(2 ** attempt)
            continue
        if kind == "no-captions":
            skip(src)
            break
        raise

Prevention

When it happens

Trigger: youtube-transcript-api raising network errors, rate limiting / IP blocks from YouTube (common on datacenter IPs, 'requests to youtube... blocked' errors), or API version incompatibilities after youtube-transcript-api's 1.x redesign (e.g. YouTubeTranscriptApi() constructor vs old static .list usage). The wrapped 'No transcript available' case is the other major trigger.

Common situations: Server-side scraping at volume triggering YouTube IP blocks; upgrading youtube-transcript-api past a major version where the internal API changed; running from cloud VMs whose IP ranges are challenged; transient network outages.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/3582a3b98bb2aac0. Report an issue: GitHub.