{"record":{"id":"4bc00d536107f233","repo":"ytdl-org/youtube-dl","slug":"s-said-s-4bc00d","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/teachertube.py","lineNumber":55,"sourceCode":"            'ext': 'mp3',\n            'title': 'PER ASPERA AD ASTRA',\n            'description': 'RADIJSKA EMISIJA ZRAKOPLOVNE TEHNI?KE ?KOLE P',\n        },\n    }, {\n        # unavailable video\n        'url': 'http://www.teachertube.com/video/intro-video-schleicher-297790',\n        'only_matching': True,\n    }]\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        webpage = self._download_webpage(url, video_id)\n\n        error = self._search_regex(\n            r'<div\\b[^>]+\\bclass=[\"\\']msgBox error[^>]+>([^<]+)', webpage,\n            'error', default=None)\n        if error:\n            raise ExtractorError('%s said: %s' % (self.IE_NAME, error), expected=True)\n\n        title = self._html_search_meta('title', webpage, 'title', fatal=True)\n        TITLE_SUFFIX = ' - TeacherTube'\n        if title.endswith(TITLE_SUFFIX):\n            title = title[:-len(TITLE_SUFFIX)].strip()\n\n        description = self._html_search_meta('description', webpage, 'description')\n        if description:\n            description = description.strip()\n\n        quality = qualities(['mp3', 'flv', 'mp4'])\n\n        media_urls = re.findall(r'data-contenturl=\"([^\"]+)\"', webpage)\n        media_urls.extend(re.findall(r'var\\s+filePath\\s*=\\s*\"([^\"]+)\"', webpage))\n        media_urls.extend(re.findall(r'\\'file\\'\\s*:\\s*[\"\\']([^\"\\']+)[\"\\'],', webpage))\n\n        formats = [\n            {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/teachertube.py#L37-L73","documentation":"Raised by TeacherTubeIE when the video page contains a div whose class starts 'msgBox error'; the matched inner text is relayed as 'TeacherTube said: <error>' with expected=True. The site itself reports a problem for that video id before any metadata parsing happens.","triggerScenarios":"Extracting www.teachertube.com/video/.../<id> where the rendered page carries an error msgBox — typically removed/unavailable videos or server-side access errors.","commonSituations":"Videos deleted by the uploader or moderated off the platform; school-district content filters serving error pages; stale links in LMS courses.","solutions":["Read the relayed message — it is TeacherTube's own error text for that video.","Verify the URL in a browser; if the video is gone, source it from the teacher's re-upload or the district's channel.","For crawlers, catch this expected error and mark the link dead rather than retrying."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"html = ydl.urlopen(url).read().decode('utf-8', 'replace')\nimport re\nm = re.search(r'<div\\b[^>+\\bclass=[\"\\']msgBox error[^>]+>([^<]+)', html)\nif m:\n    print('TeacherTube reported:', m.group(1))","typeGuard":null,"tryCatchPattern":"try:\n    info = ydl.extract_info(url)\nexcept ExtractorError as e:\n    if e.expected and 'said:' in str(e):\n        log.info('TeacherTube refused %s: %s', url, str(e))\n        return None\n    raise","preventionTips":["Treat 'said:' errors as authoritative site messages, not bugs.","Validate TeacherTube links in a browser before batch downloading.","Expect school-platform content to disappear; keep alternates."],"tags":["site-message","site-unavailable","expected-error"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}