ytdl-org/youtube-dl · error · ExtractorError
%s returned error: %s
Error message
%s returned error: %s
What it means
Raised by the Globo extractor when the security-hash endpoint (security.video.globo.com/videos/<id>/hash) returns JSON with no 'hash' but a 'message' — the API's explicit rejection (e.g. resource not entitled, signature expired). Marked expected=True; if there is no message the code just skips the resource.
Source
Thrown at youtube_dl/extractor/globo.py:131
if resource_type == 'subtitle':
subtitles.setdefault(resource.get('language') or 'por', []).append({
'url': resource_url,
})
continue
security = self._download_json(
'http://security.video.globo.com/videos/%s/hash' % video_id,
video_id, 'Downloading security hash for %s' % resource_id, query={
'player': 'desktop',
'version': '5.19.1',
'resource_id': resource_id,
})
security_hash = security.get('hash')
if not security_hash:
message = security.get('message')
if message:
raise ExtractorError(
'%s returned error: %s' % (self.IE_NAME, message), expected=True)
continue
hash_code = security_hash[:2]
padding = '%010d' % random.randint(1, 10000000000)
if hash_code in ('04', '14'):
received_time = security_hash[3:13]
received_md5 = security_hash[24:]
hash_prefix = security_hash[:23]
elif hash_code in ('02', '12', '03', '13'):
received_time = security_hash[2:12]
received_md5 = security_hash[22:]
padding += '1'
hash_prefix = '05' + security_hash[:22]
padded_sign_time = compat_str(int(received_time) + 86400) + padding
md5_data = (received_md5 + padded_sign_time + '0xAC10FD').encode()
signed_md5 = base64.urlsafe_b64encode(hashlib.md5(md5_data).digest()).decode().strip('=')View on GitHub (pinned to 956b8c5855)
Solutions
- Log in again with valid credentials so a fresh GLBID cookie is set
- Verify your Globoplay subscription covers the specific video/resource
- Update youtube-dl/yt-dlp in case the hash endpoint parameters changed
- Try a different quality/resource variant of the same video if available
Defensive patterns
Strategy: try-catch
Validate before calling
import requests
s = requests.get(f'http://security.video.globo.com/videos/{vid}/hash', params={'player': 'desktop', 'version': '5.19.1', 'resource_id': rid}).json()
if not s.get('hash'):
print('Hash refused:', s.get('message', '(no message)')) Type guard
def hash_issued(security):
return isinstance(security, dict) and isinstance(security.get('hash'), str) and len(security['hash']) >= 22 Try / catch
try:
ydl.extract_info(url)
except DownloadError as e:
if 'returned error' in str(e) and 'glb' in str(e).lower():
reauth_and_retry(url) # usually entitlement/session
else:
raise Prevention
- Keep the GLBID cookie fresh — stale sessions are the top cause of hash refusal
- Match subscription tier to content before queueing premium resources
When it happens
Trigger: Requesting a playback hash for a specific resource_id where Globo refuses: unentitled account for that media resource, expired GLBID cookie, or resource removed from the CDN.
Common situations: Subscription-tier mismatch (account lacks the package for that resource), stale login cookie after password change, or Globo tightening hash issuance for older videos.
Related errors
- This video is only available via cable service provider subs
- ', '.join([e['message'] for e in entitlement_issues])
- resp.get('userMessage') or resp['id']
- %s said: %s
- response['body']['message']
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/a32e28c437cd43ca.
Report an issue: GitHub.