ytdl-org/youtube-dl · error · ExtractorError

%s returns error %d

Error message

%s returns error %d

What it means

The second branch of BiliBiliBangumiIE._report_error: the API result dict has no 'message' key but does have a 'code' key, so the error reports the numeric code ('%s returns error %d'). Expected=True. This branch typically fires when the API signals failure with only a status code and no human-readable text.

Source

Thrown at youtube_dl/extractor/bilibili.py:119

            },
            'params': {
                'skip_download': True,  # Test metadata only
            },
        }]
    }, {
        # new BV video id format
        'url': 'https://www.bilibili.com/video/BV1JE411F741',
        'only_matching': True,
    }]

    _APP_KEY = 'iVGUTjsxvpLeuDCf'
    _BILIBILI_KEY = 'aHRmhWMLkdeMuILqORnYZocwMBpMEOdt'

    def _report_error(self, result):
        if 'message' in result:
            raise ExtractorError('%s said: %s' % (self.IE_NAME, result['message']), expected=True)
        elif 'code' in result:
            raise ExtractorError('%s returns error %d' % (self.IE_NAME, result['code']), expected=True)
        else:
            raise ExtractorError('Can\'t extract Bangumi episode ID')

    def _real_extract(self, url):
        url, smuggled_data = unsmuggle_url(url, {})

        mobj = re.match(self._VALID_URL, url)
        video_id = mobj.group('id') or mobj.group('id_bv')
        anime_id = mobj.group('anime_id')
        webpage = self._download_webpage(url, video_id)

        if 'anime/' not in url:
            cid = self._search_regex(
                r'\bcid(?:["\']:|=)(\d+)', webpage, 'cid',
                default=None
            ) or compat_parse_qs(self._search_regex(
                [r'EmbedPlayer\([^)]+,\s*"([^"]+)"\)',
                 r'EmbedPlayer\([^)]+,\s*\\"([^"]+)\\"\)',

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Map the code: common Bilibili codes are -400 (bad request), -404 (not found), -799 (rate limit); fix the request accordingly.
  2. Verify the bangumi id by opening the page in a browser and comparing with the extractor's captured anime/episode id.
  3. If code arrives as a string in newer API versions, coerce before formatting (%d on a str raises) and report the schema change upstream.
  4. For rate limiting (-799 style), back off and retry rather than hammering.

Example fix

// before
elif 'code' in result:
    raise ExtractorError('%s returns error %d' % (self.IE_NAME, result['code']), expected=True)

// after: tolerate string codes and show them safely
elif 'code' in result:
    code = result['code']
    raise ExtractorError('%s returns error %s' % (self.IE_NAME, code), expected=True)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    ydl.extract_info(bangumi_url)
except ExtractorError as e:
    m = re.search(r'returns error (-?\d+)', str(e))
    if m:
        code = int(m.group(1))
        if code in (-799,):  # rate limit style
            backoff_and_retry(bangumi_url)
        elif code == -404:
            mark_not_found(bangumi_url)
    else:
        raise

Prevention

When it happens

Trigger: A Bangumi API response shaped like {'code': -404, ...} or {'code': <negative/nonzero>} without a message — unknown season/episode ids, expired signed URLs, or API versions that omit the message field. The %d format also implies code must be an integer; a string code would raise a format error instead.

Common situations: Wrong anime_id/episode_id parsed from the URL; API contract change dropping the message field; signed-request staleness; bangumi entries delisted with bare error codes.

Related errors


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