{"record":{"id":"947d259a789fc76f","repo":"ytdl-org/youtube-dl","slug":"could-not-extract-ptmd-path","errorCode":null,"errorMessage":"Could not extract ptmd_path","messagePattern":"Could not extract ptmd_path","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/zdf.py","lineNumber":265,"sourceCode":"    }]\n\n    def _extract_entry(self, url, player, content, video_id):\n        title = content.get('title') or content['teaserHeadline']\n\n        t = content['mainVideoContent']['http://zdf.de/rels/target']\n\n        def get_ptmd_path(d):\n            return (\n                d.get('http://zdf.de/rels/streams/ptmd')\n                or d.get('http://zdf.de/rels/streams/ptmd-template',\n                         '').replace('{playerId}', 'ngplayer_2_4'))\n\n        ptmd_path = get_ptmd_path(try_get(t, lambda x: x['streams']['default'], dict) or {})\n        if not ptmd_path:\n            ptmd_path = get_ptmd_path(t)\n\n        if not ptmd_path:\n            raise ExtractorError('Could not extract ptmd_path')\n\n        info = self._extract_ptmd(\n            urljoin(url, ptmd_path), video_id, player['apiToken'], url)\n\n        thumbnails = []\n        layouts = try_get(\n            content, lambda x: x['teaserImageRef']['layouts'], dict)\n        if layouts:\n            for layout_key, layout_url in layouts.items():\n                layout_url = url_or_none(layout_url)\n                if not layout_url:\n                    continue\n                thumbnail = {\n                    'url': layout_url,\n                    'format_id': layout_key,\n                }\n                mobj = re.search(r'(?P<width>\\d+)x(?P<height>\\d+)', layout_key)\n                if mobj:","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/zdf.py#L247-L283","documentation":"Raised by the ZDF extractor when neither the 'http://zdf.de/rels/streams/ptmd' key nor the 'ptmd-template' key can be found in the JSON document (neither under t['streams']['default'] nor at the top level of t). Without the ptmd path the extractor cannot fetch the stream metadata, so it aborts.","triggerScenarios":"Downloading a ZDF/ZDFmediathek URL where the API response for the content lacks stream information: content taken offline, DRM/formats removed, an API layout change by ZDF, or an entry type (e.g. season/show page) that has no direct streams.","commonSituations":"ZDF changed their JSON API structure (this historically broke yt-dlp/youtube-dl until patched); the video expired and was removed from the mediathek; the URL points to a series instead of a single episode.","solutions":["Update youtube-dl to the latest version (ZDF API changes are fixed upstream quickly)","Verify the video still plays in a browser at the same URL","If the URL is a show/season page, open it in the browser and copy the URL of a specific episode instead","If it persists on the latest version, report it as a site-bug to the youtube-dl/yt-dlp issue tracker with the URL"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.download([url])\nexcept ExtractorError as e:\n    if 'Could not extract ptmd_path' in str(e):\n        upgrade_youtubedl_and_retry_once()  # likely an API layout change\n    else:\n        raise","preventionTips":["Pin a recent youtube-dl/yt-dlp version in automation and update when site extractors break","Verify content is still available in a browser before batch-downloading old ZDF links","Prefer episode URLs over season/show pages"],"tags":["zdf","api-change","json-parsing","extractor"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}