{"record":{"id":"1e6e3b415202b8d9","repo":"ytdl-org/youtube-dl","slug":"s-said-s-1e6e3b","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/tfo.py","lineNumber":41,"sourceCode":"            'ext': 'mp4',\n            'title': 'Video Game Hackathon',\n            'description': 'md5:558afeba217c6c8d96c60e5421795c07',\n        }\n    }\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        self._request_webpage(HEADRequest('http://www.tfo.org/'), video_id)\n        infos = self._download_json(\n            'http://www.tfo.org/api/web/video/get_infos', video_id, data=json.dumps({\n                'product_id': video_id,\n            }).encode(), headers={\n                'X-tfo-session': self._get_cookies('http://www.tfo.org/')['tfo-session'].value,\n            })\n        if infos.get('success') == 0:\n            if infos.get('code') == 'ErrGeoBlocked':\n                self.raise_geo_restricted(countries=self._GEO_COUNTRIES)\n            raise ExtractorError('%s said: %s' % (self.IE_NAME, clean_html(infos['msg'])), expected=True)\n        video_data = infos['data']\n\n        return {\n            '_type': 'url_transparent',\n            'id': video_id,\n            'url': 'limelight:media:' + video_data['llid'],\n            'title': video_data['title'],\n            'description': video_data.get('description'),\n            'series': video_data.get('collection'),\n            'season_number': int_or_none(video_data.get('season')),\n            'episode_number': int_or_none(video_data.get('episode')),\n            'duration': int_or_none(video_data.get('duration')),\n            'ie_key': 'LimelightMedia',\n        }\n","sourceCodeStart":23,"sourceCodeEnd":56,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tfo.py#L23-L56","documentation":"Raised by the TFO extractor when the TFO API responds with success == 0 and the failure is not the known geo-block code. The message embeds the extractor name and the API's own HTML-cleaned 'msg' field, so the actual reason comes from the server (e.g. expired video, removed product id). It is marked expected=True, meaning youtube-dl treats it as a content-availability error rather than an extractor bug.","triggerScenarios":"Calling the extractor on a tfo.org video whose /api/web/video/get_infos POST returns success:0 with a code other than 'ErrGeoBlocked'; e.g. a product_id that was retired or a video whose Limelight media (llid) was deleted.","commonSituations":"User passes an old TFO URL found in a playlist or forum post; TFO changes its API response shape; the 'tfo-session' cookie is missing so the API rejects the request; the video is region-locked under a code not equal to 'ErrGeoBlocked'.","solutions":["Open the same URL in a browser and confirm the video still plays on tfo.org; if it 404s, the video is gone and no tool-side fix exists","Update to the latest youtube-dl / yt-dlp, where TFO extractor changes are tracked","If the API returns ErrGeoBlocked but you still see this error, check that the initial HEADRequest to www.tfo.org succeeded and set the tfo-session cookie","Use a Canadian exit IP (VPN) since TFO is geo-restricted to _GEO_COUNTRIES"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import urllib.request, json\nreq = urllib.request.Request('http://www.tfo.org/api/web/video/get_infos', data=json.dumps({'product_id': vid}).encode())\n# a HEAD to www.tfo.org first is needed for the session cookie; if the API returns success:0 the extractor will fail","typeGuard":null,"tryCatchPattern":"from youtube_dl.utils import ExtractorError\ntry:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if e.expected and 'TFO said' in str(e):\n        mark_unavailable(url, str(e))  # server-side refusal; do not retry\n    else:\n        raise","preventionTips":["Pre-validate TFO URLs in a browser before queueing them","Treat expected=True errors from TFO as permanent for that URL","Use a Canadian exit IP for TFO content"],"tags":["extractor","api-error","content-unavailable","geo-restriction"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}