ytdl-org/youtube-dl · error · ExtractorError

self._parse_json(e.cause.read(), None)['errorMessage']

Error message

self._parse_json(e.cause.read(), None)['errorMessage']

What it means

Raised by PlayPlusTV's login when the api.playplus.tv/api/web/login PUT returns HTTP 401. The extractor reads the 401 body, parses JSON, and re-raises its 'errorMessage' field verbatim (expected). The catalog's 'message' is that expression source — the actual text is PlayPlus's (e.g. invalid credentials).

Source

Thrown at youtube_dl/extractor/playplustv.py:59

    def _real_initialize(self):
        email, password = self._get_login_info()
        if email is None:
            self.raise_login_required()

        req = PUTRequest(
            'https://api.playplus.tv/api/web/login', json.dumps({
                'email': email,
                'password': password,
            }).encode(), {
                'Content-Type': 'application/json; charset=utf-8',
            })

        try:
            self._token = self._download_json(req, None)['token']
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
                raise ExtractorError(self._parse_json(
                    e.cause.read(), None)['errorMessage'], expected=True)
            raise

        self._profile = self._call_api('Profiles')['list'][0]['_id']

    def _real_extract(self, url):
        project_id, media_id = re.match(self._VALID_URL, url).groups()
        media = self._call_api(
            'Media', media_id, {
                'profileId': self._profile,
                'projectId': project_id,
                'mediaId': media_id,
            })['obj']
        title = media['title']

        formats = []
        for f in media.get('files', []):
            f_url = f.get('url')

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Verify email/password by logging in at playplus.tv in a browser
  2. Confirm the subscription is active — expired accounts may auth but fail later at Media calls
  3. If the service is discontinued in your region, no fix is possible from the client
Defensive patterns

Strategy: validation

Validate before calling

import requests
r = requests.put('https://api.playplus.tv/api/web/login',
                 json={'email': E, 'password': P})
if r.status_code == 401:
    raise SystemExit('PlayPlus rejected credentials: ' + r.json().get('errorMessage', '?'))

Try / catch

try:
    ydl.extract_info(url)
except ExtractorError as e:
    if e.expected and ('errorMessage' in str(e) or 'credentials' in str(e).lower()):
        sys.exit('Fix PlayPlus credentials in .netrc')

Prevention

When it happens

Trigger: PUT to the login endpoint with email/password the service rejects → 401 with a JSON body containing errorMessage. Only occurs when credentials are supplied (login is mandatory; _call_api needs the token for Profiles/Media).

Common situations: Wrong password in .netrc; subscription lapsed so the account can't get a web token; regional PlayPlus (Brazil) service closure; body not JSON on some failures would instead raise a parse error.

Related errors


AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14). Data as JSON: /api/errors/61e39b400d20c313. Report an issue: GitHub.