ytdl-org/youtube-dl · warning · ExtractorError
message (dynamic from service error response, error.get('mes
Error message
message (dynamic from service error response, error.get('message')) What it means
Raised by the fallback _unlock_file stub on platforms without fcntl/msvcrt (the same ImportError branch as the lock stub). Release of a locked_file raises IOError(UNSUPPORTED_MSG). In practice you normally hit the lock-side error first, so this one surfaces mainly when code constructs or patches around locking but still uses the unlock path (e.g. locked_file.__exit__ after a no-op lock was injected).
Source
Thrown at youtube_dl/extractor/adn.py:225
'withMetadata': 'true',
'source': 'Web'
})
break
except ExtractorError as e:
if not isinstance(e.cause, compat_HTTPError):
raise e
if e.cause.code == 401:
# This usually goes away with a different random pkcs1pad, so retry
continue
error = self._parse_json(
self._webpage_read_content(e.cause, links_url, video_id),
video_id, fatal=False) or {}
message = error.get('message')
if e.cause.code == 403 and error.get('code') == 'player-bad-geolocation-country':
self.raise_geo_restricted(msg=message)
raise ExtractorError(message)
else:
raise ExtractorError('Giving up retrying')
links = links_data.get('links') or {}
metas = links_data.get('metadata') or {}
sub_url = (links.get('subtitles') or {}).get('all')
video_info = links_data.get('video') or {}
title = metas['title']
formats = []
for format_id, qualities in (links.get('streaming') or {}).items():
if not isinstance(qualities, dict):
continue
for quality, load_balancer_url in qualities.items():
load_balancer_data = self._download_json(
load_balancer_url, video_id,
'Downloading %s %s JSON metadata' % (format_id, quality),
fatal=False) or {}View on GitHub (pinned to 956b8c5855)
Solutions
- Monkeypatch both stubs symmetrically: utils._lock_file = lambda f, e: None and utils._unlock_file = lambda f: None.
- Prefer running under CPython where real locking exists rather than patching half the API.
- Avoid calling _lock_file/_unlock_file directly; use the locked_file context manager so both sides stay consistent.
- Update youtube-dl / use yt-dlp, whose file handling no longer relies on these paths in the same way.
Example fix
# before (partial patch — unlock still raises) youtube_dl.utils._lock_file = lambda f, exclusive: None # after (patch both sides) youtube_dl.utils._lock_file = lambda f, exclusive: None youtube_dl.utils._unlock_file = lambda f: None
Defensive patterns
Strategy: try-catch
Validate before calling
def file_locking_supported():
if sys.platform == 'win32':
return True
try:
import fcntl # noqa: F401
return True
except ImportError:
return False Try / catch
try:
with locked_file(path, 'w') as f:
f.write(data)
except IOError as e:
if 'file locking is not supported' in str(e):
with open(path, 'w') as f: # accept single-process, unlocked fallback
f.write(data)
else:
raise Prevention
- When monkeypatching the locking stubs, always replace _lock_file AND _unlock_file together.
- Use the locked_file context manager rather than calling _unlock_file directly.
- Probe for fcntl at startup on non-Windows platforms and document the limitation.
When it happens
Trigger: On a no-fcntl runtime: entering any locked_file would already fail at _lock_file; this specific raise appears when _lock_file was monkeypatched/bypassed but __exit__ still calls the stub _unlock_file, or when library code calls _unlock_file directly.
Common situations: Embedders on Jython/IronPython who patched only one of the two stubs; copy-pasted locking wrappers calling utils._unlock_file directly; test environments simulating platforms by hiding fcntl.
Related errors
- %s said: %s
- Playlist end must be greater than playlist start
- Not removing directory %s - this does not look like a cache
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/971a673c23a5a0da.
Report an issue: GitHub.