{"record":{"id":"750c7e65c68c3deb","repo":"ytdl-org/youtube-dl","slug":"login-failed-s-said-r","errorCode":null,"errorMessage":"Login failed, %s said: %r","messagePattern":"Login failed, (.+?) said: %r","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/tennistv.py","lineNumber":58,"sourceCode":"            'Email': username,\n            'Password': password,\n        }\n        login_json = json.dumps(login_form).encode('utf-8')\n        headers = {\n            'content-type': 'application/json',\n            'Referer': 'https://www.tennistv.com/login',\n            'Origin': 'https://www.tennistv.com',\n        }\n\n        login_result = self._download_json(\n            'https://www.tennistv.com/api/users/v1/login', None,\n            note='Logging in',\n            errnote='Login failed (wrong password?)',\n            headers=headers,\n            data=login_json)\n\n        if login_result['error']['errorCode']:\n            raise ExtractorError('Login failed, %s said: %r' % (self.IE_NAME, login_result['error']['errorMessage']))\n\n        if login_result['entitlement'] != 'SUBSCRIBED':\n            self.report_warning('%s may not be subscribed to %s.' % (username, self.IE_NAME))\n\n        self._session_token = login_result['sessionToken']\n\n    def _real_initialize(self):\n        self._login()\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        webpage = self._download_webpage(url, video_id)\n\n        internal_id = self._search_regex(r'video=([0-9]+)', webpage, 'internal video id')\n\n        headers = {\n            'Origin': 'https://www.tennistv.com',\n            'authorization': 'ATP %s' % self._session_token,","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tennistv.py#L40-L76","documentation":"Raised by TennisTVIE._login when the POST to tennistv.com/api/users/v1/login succeeds at HTTP level but the JSON body has a truthy error.errorCode. The API's errorMessage is embedded as 'Login failed, TennisTV said: <message>'. Unlike other login errors here it is NOT marked expected=True, so youtube-dl logs it as a login error with the site's reason.","triggerScenarios":"Submitting credentials that the TennisTV login API rejects: wrong password (errorCode set), unknown email, or account-level refusal; also fires when the API changes its error envelope shape so error.errorCode becomes truthy unexpectedly.","commonSituations":"Expired/changed passwords; API contract changes after TennisTV site updates; accounts locked for too many attempts; free accounts trying to fetch subscriber content.","solutions":["Read the relayed errorMessage — it is TennisTV's exact refusal reason; fix credentials accordingly.","Verify sign-in at https://www.tennistv.com/login in a browser before blaming the tool.","Confirm your subscription is active; a lapsed subscription can produce API-level login errors.","If credentials are right but it still fails, the API schema likely changed — upgrade youtube-dl/yt-dlp or switch to cookie-based auth (--cookies from a logged-in browser session)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'Login failed, TennisTV said' in str(e):\n        log.error('TennisTV API: %s', str(e))  # contains errorMessage\n        prompt_reauth()\n    else:\n        raise","preventionTips":["Verify TennisTV credentials in a browser after any password change.","Keep subscriptions active; API login errors often accompany lapsed entitlements.","If the error envelope looks garbled, the API changed — upgrade the extractor."],"tags":["login","api-error","credentials","unexpected-error"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}