ytdl-org/youtube-dl · error · ExtractorError
json.loads(e.cause.read().decode())['message']
Error message
json.loads(e.cause.read().decode())['message']
What it means
VRV OAuth-signature error path: when the signed API request (HMAC-SHA1 over base_url+query with oAuthSecret & token secret) returns HTTP 401, the extractor reads the error body, parses it as JSON, and re-raises its 'message' field with expected=True. The placeholder stands for that parsed message (e.g. 'Invalid token').
Source
Thrown at youtube_dl/extractor/vrv.py:62
headers = self.geo_verification_headers()
if data:
data = json.dumps(data).encode()
headers['Content-Type'] = 'application/json'
base_string = '&'.join([
'POST' if data else 'GET',
compat_urllib_parse.quote(base_url, ''),
compat_urllib_parse.quote(encoded_query, '')])
oauth_signature = base64.b64encode(hmac.new(
(self._API_PARAMS['oAuthSecret'] + '&' + self._TOKEN_SECRET).encode('ascii'),
base_string.encode(), hashlib.sha1).digest()).decode()
encoded_query += '&oauth_signature=' + compat_urllib_parse.quote(oauth_signature, '')
try:
return self._download_json(
'?'.join([base_url, encoded_query]), video_id,
note='Downloading %s JSON metadata' % note, headers=headers, data=data)
except ExtractorError as e:
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
raise ExtractorError(json.loads(e.cause.read().decode())['message'], expected=True)
raise
def _call_cms(self, path, video_id, note):
if not self._CMS_SIGNING:
index = self._call_api('index', video_id, 'CMS Signing')
self._CMS_SIGNING = index.get('cms_signing') or {}
if not self._CMS_SIGNING:
for signing_policy in index.get('signing_policies', []):
signing_path = signing_policy.get('path')
if signing_path and signing_path.startswith('/cms/'):
name, value = signing_policy.get('name'), signing_policy.get('value')
if name and value:
self._CMS_SIGNING[name] = value
return self._download_json(
self._API_DOMAIN + path, video_id, query=self._CMS_SIGNING,
note='Downloading %s JSON metadata' % note, headers=self.geo_verification_headers())
def _get_cms_resource(self, resource_key, video_id):View on GitHub (pinned to 956b8c5855)
Solutions
- Re-authenticate: pass fresh credentials/cookies so a new token secret is derived
- Check the parsed message text — 'Invalid token' vs other messages point to different fixes
- Ensure requests come from a supported region (VRV is US/Canada) with a sane system clock
Defensive patterns
Strategy: retry
Try / catch
from youtube_dl.utils import ExtractorError
try:
ydl.extract_info(url)
except ExtractorError as e:
if 'token' in str(e).lower():
reauth_vrv() # obtain fresh token secret
ydl.extract_info(url) # single retry with new credentials Prevention
- Refresh VRV auth tokens proactively — they expire
- Keep system clock accurate; OAuth signatures break on skew
- Read the parsed 401 body message; it names the exact credential problem
When it happens
Trigger: Calling the VRV API with a stale/invalid session token or bad OAuth credentials, so the server answers 401 and the except-branch converts e.cause (compat_HTTPError) into the JSON message.
Common situations: Expired VRV session/auth token after re-login requirements; region outside US/Canada where VRV blocks auth; clock skew breaking the OAuth signature (timestamp/nonce).
Related errors
- error (parsed JSON 'Message' from 401 response)
- resp.get('userMessage') or resp['id']
- %s said: %s
- %s
- message
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/b309837434a68a26.
Report an issue: GitHub.