{"record":{"id":"143aa3b8be9c828f","repo":"ytdl-org/youtube-dl","slug":"message-143aa3","errorCode":null,"errorMessage":"message","messagePattern":"message","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/packtpub.py","lineNumber":65,"sourceCode":"    }]\n    _NETRC_MACHINE = 'packtpub'\n    _TOKEN = None\n\n    def _real_initialize(self):\n        username, password = self._get_login_info()\n        if username is None:\n            return\n        try:\n            self._TOKEN = self._download_json(\n                'https://services.packtpub.com/auth-v1/users/tokens', None,\n                'Downloading Authorization Token', data=json.dumps({\n                    'username': username,\n                    'password': password,\n                }).encode())['data']['access']\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code in (400, 401, 404):\n                message = self._parse_json(e.cause.read().decode(), None)['message']\n                raise ExtractorError(message, expected=True)\n            raise\n\n    def _real_extract(self, url):\n        course_id, chapter_id, video_id, display_id = re.match(self._VALID_URL, url).groups()\n\n        headers = {}\n        if self._TOKEN:\n            headers['Authorization'] = 'Bearer ' + self._TOKEN\n        try:\n            video_url = self._download_json(\n                'https://services.packtpub.com/products-v1/products/%s/%s/%s' % (course_id, chapter_id, video_id), video_id,\n                'Downloading JSON video', headers=headers)['data']\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 400:\n                self.raise_login_required('This video is locked')\n            raise\n\n        # TODO: find a better way to avoid duplicating course requests","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/packtpub.py#L47-L83","documentation":"Raised by the PacktPub extractor's login helper when the auth-v1 token endpoint returns HTTP 400/401/404. The extractor reads the response body and re-raises the server's own 'message' field as an expected ExtractorError. The literal 'message' in the catalog is the format source: the actual text (e.g. 'Invalid username or password') comes from Packt's API.","triggerScenarios":"Calling PacktPub extraction with --username/--password (or netrc) where services.packtpub.com/auth-v1/users/tokens rejects the credentials with 400/401/404; or when the auth endpoint moves/changes shape (404 body may not even be JSON, which would instead cause a parse error).","commonSituations":"Wrong or expired Packt login in .netrc; ebook-claim-only accounts lacking video access; API v1 being retired by Packt so the 404 path triggers; password containing characters mangled by JSON encoding.","solutions":["Verify the Packt credentials by logging in at packtpub.com in a browser, then correct --username/---password or .netrc","If credentials are correct, suspect the auth-v1 endpoint is gone — update youtube-dl/yt-dlp to a version with the fixed Packt extractor","Test extraction anonymously (no credentials) since some free content works without a token"],"exampleFix":"# before\nyoutube_dl --username me@example.com --password 'wrong' 'https://www.packtpub.com/application-development/some-video'\n# after\nyoutube_dl --netrc 'https://www.packtpub.com/application-development/some-video'  # with verified 'machine packtpub login me@example.com password correct' in ~/.netrc","handlingStrategy":"validation","validationCode":"// Verify credentials work before batch extraction\ncurl -s -o /dev/null -w '%{http_code}' -X POST https://services.packtpub.com/auth-v1/users/tokens -d '{\"username\":\"me@example.com\",\"password\":\"hunter2\"}'\n// 200 → credentials fine; 400/401/404 → fix before invoking yt-dlp","typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if e.expected:  # server-supplied auth message\n        print('Packt login rejected:', str(e))","preventionTips":["Keep .netrc credentials in sync with the real account","Smoke-test auth with a single URL before long batches","Update extractors when vendors retire auth API versions"],"tags":["packtpub","authentication","http-401","extractor-error","expected"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}