ytdl-org/youtube-dl · error · ExtractorError

unable to download video webpage: %s

Error message

unable to download video webpage: %s

What it means

SoundcloudSetIE resolves a playlist/sets URL through the Soundcloud API; if the resolved info dict contains an 'errors' list, each entry's 'error_message' is joined and raised as 'unable to download video webpage: <msgs>'. This surfaces Soundcloud's own playlist-level failures (most commonly 'Not Found' / 404 for deleted or private sets).

Source

Thrown at youtube_dl/extractor/soundcloud.py:554

    }, {
        'url': 'https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep/token',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = re.match(self._VALID_URL, url)

        full_title = '%s/sets/%s' % mobj.group('uploader', 'slug_title')
        token = mobj.group('token')
        if token:
            full_title += '/' + token

        info = self._download_json(self._resolv_url(
            self._BASE_URL + full_title), full_title)

        if 'errors' in info:
            msgs = (compat_str(err['error_message']) for err in info['errors'])
            raise ExtractorError('unable to download video webpage: %s' % ','.join(msgs))

        return self._extract_set(info, token)


class SoundcloudPagedPlaylistBaseIE(SoundcloudIE):
    def _extract_playlist(self, base_url, playlist_id, playlist_title):
        # Per the SoundCloud documentation, the maximum limit for a linked partitioning query is 200.
        # https://developers.soundcloud.com/blog/offset-pagination-deprecated
        COMMON_QUERY = {
            'limit': 200,
            'linked_partitioning': '1',
        }

        query = COMMON_QUERY.copy()
        query['offset'] = 0

        next_href = base_url

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Open the playlist URL in a browser to check whether it still exists / is public.
  2. If private, obtain the full share link including the secret token path segment and use that exact URL.
  3. If the uploader renamed, find their current handle and rebuild the URL.
  4. Update yt-dlp - resolver contract changes also surface through this error.
Defensive patterns

Strategy: try-catch

Validate before calling

info = resolve(full_title)
if 'errors' in info:
    msgs = [e.get('error_message') for e in info['errors']]
    if any('404' in str(m) or 'Not Found' in str(m) for m in msgs):
        drop(url)

Type guard

def has_resolver_errors(info: dict) -> bool:
    return 'errors' in info and bool(info['errors'])

Try / catch

try:
    extract(url)
except ExtractorError as e:
    if 'unable to download video webpage' in str(e):
        check_playlist_visibility(url)
    else:
        raise

Prevention

When it happens

Trigger: Resolving <uploader>/sets/<slug>[/<token>] where the API returns an errors array instead of playlist data: deleted playlist, private playlist accessed without its secret token, renamed uploader making the path stale.

Common situations: Deleted or made-private playlists (owner deleted the set or flipped visibility); a copied URL missing the secret token suffix; uploader renamed their account so the uploader segment of the URL no longer resolves; region/network blocks making the resolver return an error payload.

Related errors


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