{"record":{"id":"e47a48211d7c8b64","repo":"ytdl-org/youtube-dl","slug":"s-said-s-e47a48","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/cbc.py","lineNumber":261,"sourceCode":"        return resp['signature']\n\n    def _call_api(self, path, video_id):\n        url = path if path.startswith('http') else self._API_BASE_URL + path\n        for _ in range(2):\n            try:\n                result = self._download_xml(url, video_id, headers={\n                    'X-Clearleap-DeviceId': self._device_id,\n                    'X-Clearleap-DeviceToken': self._device_token,\n                })\n            except ExtractorError as e:\n                if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:\n                    # Device token has expired, re-acquiring device token\n                    self._register_device()\n                    continue\n                raise\n        error_message = xpath_text(result, 'userMessage') or xpath_text(result, 'systemMessage')\n        if error_message:\n            raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message))\n        return result\n\n    def _real_initialize(self):\n        if self._valid_device_token():\n            return\n        device = self._downloader.cache.load(\n            'cbcwatch', self._cache_device_key()) or {}\n        self._device_id, self._device_token = device.get('id'), device.get('token')\n        if self._valid_device_token():\n            return\n        self._register_device()\n\n    def _valid_device_token(self):\n        return self._device_id and self._device_token\n\n    def _cache_device_key(self):\n        email, _ = self._get_login_info()\n        return '%s_device' % hashlib.sha256(email.encode()).hexdigest() if email else 'device'","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/cbc.py#L243-L279","documentation":"Raised by CBC's watch (Clearleap-based) device API layer when the returned XML contains a <userMessage> or <systemMessage> element. The message format is '<IE_NAME> said: <message>'. These elements are how the CBC/Clearleap backend reports application-level failures (expired tokens, unavailable content, entitlement problems) inside an HTTP-200 response.","triggerScenarios":"Any successful HTTP response from the CBC watch XML API whose body carries userMessage/systemMessage — e.g. an invalid or revoked device token that still passes the 401 check, content not entitled to the account/region, or a video id the backend rejects. Note a real 401 is retried transparently after re-registering the device.","commonSituations":"Device registration cache stale or invalidated by CBC; content geo- or entitlement-restricted; API changes on CBC's side breaking the request so the backend replies with an error envelope.","solutions":["Clear the cached device credentials (youtube-dl --no-cache-dir or remove the 'cbcwatch' cache entry) so a fresh device is registered.","Read the userMessage text — it usually states the real cause (e.g. 'not available in your region', 'token expired').","Update to yt-dlp; CBC's platform changed substantially after this extractor was written.","If entitlement-gated, ensure you are entitled (sign in / region) before extracting."],"exampleFix":"# before\nydl.extract(cbc_watch_url)  # 'CBC said: <backend message>'\n\n# after\nfrom youtube_dl import YoutubeDL\nwith YoutubeDL({'no-cache-dir': True}) as ydl:  # force device re-registration\n    ydl.extract(cbc_watch_url)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from youtube_dl.utils import ExtractorError\nfor attempt in range(2):\n    try:\n        ydl.extract_info(url)\n        break\n    except ExtractorError as e:\n        if 'said:' in str(e) and attempt == 0:\n            # device token may be stale -> clear cache and re-register once\n            ydl = YoutubeDL({'no-cache-dir': True})\n            continue\n        raise","preventionTips":["Cache CBC device tokens yourself but invalidate aggressively.","Parse the 'userMessage' text to decide retry vs permanent failure.","Treat CBC platform changes as likely — prefer maintained forks."],"tags":["cbc","clearleap","xml","device-token"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}