ytdl-org/youtube-dl · error · ValueError

Invalid dfxp/TTML subtitle

Error message

Invalid dfxp/TTML subtitle

What it means

Raised by dfxp2srt() in youtube_dl.utils after parsing subtitle XML: it looks for <tt:p>/<ttml:p> (or legacy <p>) paragraph elements anywhere in the document and finds none, so there is no subtitle content to convert to SRT. It signals structurally valid XML that is not a recognizable DFXP/TTML subtitle document.

Source

Thrown at youtube_dl/utils.py:5083

        def close(self):
            return self._out.strip()

    def parse_node(node):
        target = TTMLPElementParser()
        parser = xml.etree.ElementTree.XMLParser(target=target)
        parser.feed(xml.etree.ElementTree.tostring(node))
        return parser.close()

    for k, v in LEGACY_NAMESPACES:
        for ns in v:
            dfxp_data = dfxp_data.replace(ns, k)

    dfxp = compat_etree_fromstring(dfxp_data)
    out = []
    paras = dfxp.findall(_x('.//ttml:p')) or dfxp.findall('.//p')

    if not paras:
        raise ValueError('Invalid dfxp/TTML subtitle')

    repeat = False
    while True:
        for style in dfxp.findall(_x('.//ttml:style')):
            style_id = style.get('id') or style.get(_x('xml:id'))
            if not style_id:
                continue
            parent_style_id = style.get('style')
            if parent_style_id:
                if parent_style_id not in styles:
                    repeat = True
                    continue
                styles[style_id] = styles[parent_style_id].copy()
            for prop in SUPPORTED_STYLING:
                prop_val = style.get(_x('tts:' + prop))
                if prop_val:
                    styles.setdefault(style_id, {})[prop] = prop_val
        if repeat:

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Inspect the downloaded .ttml file to confirm it actually contains <p> subtitle elements.
  2. If the content is another format (vtt/srt/json), convert with the appropriate path or keep it unconverted (--convert-subs not set to srt).
  3. If namespaces changed, extend LEGACY_NAMESPACES in youtube_dl/utils.py to map the new namespace to ttml.
  4. Re-fetch the subtitle URL manually (browser or curl) to check for HTML error pages; fix auth/cookies if that is the cause.

Example fix

# before
from youtube_dl.utils import dfxp2srt
srt = dfxp2srt(xml_text)  # ValueError: Invalid dfxp/TTML subtitle — no <p> elements

# after
import xml.etree.ElementTree as ET
from youtube_dl.utils import dfxp2srt
root = ET.fromstring(xml_text)
if not (root.findall('.//{http://www.w3.org/ns/ttml}p') or root.findall('.//p')):
    raise ValueError('source is not a TTML subtitle document; keeping original')
srt = dfxp2srt(xml_text)
Defensive patterns

Strategy: validation

Validate before calling

import xml.etree.ElementTree as ET

def looks_like_ttml(data):
    try:
        root = ET.fromstring(data)
    except ET.ParseError:
        return False
    return bool(root.findall('.//{http://www.w3.org/ns/ttml}p') or root.findall('.//p'))

# call before dfxp2srt:
# if not looks_like_ttml(data): keep original subtitle file

Try / catch

from youtube_dl.utils import dfxp2srt, XAttrUnavailableError  # ValueError is not custom — catch broadly
try:
    srt = dfxp2srt(ttml_bytes)
except ValueError:
    # keep the original subtitle file, do not convert
    srt = None

Prevention

When it happens

Trigger: Calling dfxp2srt(dfxp_data) where dfxp_data is XML lacking any p elements: an HTML error page saved as .ttml, a ttml file whose paragraphs use a different namespace not covered by LEGACY_NAMESPACES substitution, an empty <tt/> skeleton, or binary/garbage data that ElementTree still parsed (or that became an empty root after namespace mangling).

Common situations: A subtitle URL returns an error page or login wall instead of TTML; a site renames subtitle namespaces or serves WebVTT/JSON mislabeled as ttml; downloading subtitles with --write-sub --convert-subs srt for a format the converter does not recognize.

Related errors


AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14). Data as JSON: /api/errors/7d07af0baa53c0a4. Report an issue: GitHub.