yt-dlp/yt-dlp · error · ExtractorError
failure
Error message
failure
What it means
PikselIE._call_api (yt_dlp/extractor/piksel.py:78) wraps the Piksel player API at https://player.piksel.tech/ws/ws_<resource>/api/<app_token>/mode/json/apiv/5. It inspects the JSON envelope: if response contains a failure.reason, that reason string is re-raised verbatim as an expected ExtractorError; if the response object is empty, the message is 'Empty response from API'. With fatal=False the same condition is only a warning and extraction continues.
Source
Thrown at yt_dlp/extractor/piksel.py:78
'upload_date': '20170204',
'thumbnail': 'https://thumbs.piksel.tech/thumbs/aid/t1495569155/3279887.jpg?w=640&h=360',
},
},
{
# https://www3.nhk.or.jp/nhkworld/en/ondemand/video/2019240/
'url': 'http://player.piksel.com/v/refid/nhkworld/prefid/nw_vod_v_en_2019_240_20190823233000_02_1566873477',
'only_matching': True,
},
]
def _call_api(self, app_token, resource, display_id, query, host='https://player.piksel.tech', fatal=True):
url = urljoin(host, f'/ws/ws_{resource}/api/{app_token}/mode/json/apiv/5')
response = traverse_obj(
self._download_json(url, display_id, query=query, fatal=fatal), ('response', {dict})) or {}
failure = traverse_obj(response, ('failure', 'reason')) if response else 'Empty response from API'
if failure:
if fatal:
raise ExtractorError(failure, expected=True)
self.report_warning(failure)
return response
def _real_extract(self, url):
ref_id, display_id = self._match_valid_url(url).groups()
webpage = self._download_webpage(url, display_id)
app_token = self._search_regex([
r'clientAPI\s*:\s*"([^"]+)"',
r'data-de-api-key\s*=\s*"([^"]+)"',
], webpage, 'app token')
query = {'refid': ref_id, 'prefid': display_id} if ref_id else {'v': display_id}
program = self._call_api(
app_token, 'program', display_id, query, url)['WsProgramResponse']['program']
video_id = program['uuid']
video_data = program['asset']
title = video_data['title']
asset_type = dict_get(video_data, ['assetType', 'asset_type'])
View on GitHub (pinned to 81ecd58b13)
Solutions
- Read the actual failure reason in the message — it comes straight from the Piksel API and names the real problem (expired asset, bad ref, auth).
- Verify the refid/prefid in the URL are still valid by opening the player page in a browser.
- Update yt-dlp; the app_token regexes (clientAPI / data-de-api-key) have been adjusted before when Piksel pages changed.
- If the API host changed, check the extractor source for the current default host of _call_api.
Defensive patterns
Strategy: try-catch
Type guard
from yt_dlp.utils import ExtractorError
def is_piksel_api_failure(e: BaseException) -> bool:
"""True when the Piksel API returned a failure envelope or empty response."""
return isinstance(e, ExtractorError) and (
'Empty response from API' in str(e) or e.expected) Try / catch
from yt_dlp.utils import ExtractorError
try:
info = ydl.extract_info(url)
except ExtractorError as e:
# e carries the API's own failure.reason text; log it verbatim and give up on this asset
logger.warning('piksel api refused %s: %s', url, e)
if not e.expected:
raise Prevention
- Treat the error text as authoritative API output — it names the actual failure reason.
- Check that Piksel player URLs still resolve in a browser before batch-downloading many of them.
- Keep yt-dlp updated; app_token scraping and the default API host have both changed historically.
When it happens
Trigger: Any Piksel-hosted player API call (used for NHK World VOD and other Piksel customers) where the API returns response.failure.reason — e.g. an expired or removed refid/prefid asset, a wrong app_token scraped from the page, or the API answering with an empty response object. The exact API reason text becomes the error message.
Common situations: Old NHK World VOD links (player.piksel.com/v/refid/nhkworld/prefid/...) whose assets expired; the data-de-api-key / clientAPI markup changed after a page redesign so the app_token is wrong; the API host moving between player.piksel.com and player.piksel.tech.
Related errors
- Mediasite says: {}
- Unable to resolve redirect URL
- Unable to extract video info
- Unexpected content type {content_type!r}
- Unable to construct playlist URL
AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22).
Data as JSON: /api/errors/af81827a1aca8491.
Report an issue: GitHub.