Skip to content

[BUG] Qobuz: every album download crashes with KeyError 'tracks'; playlists silently download 0 tracks (server-side API change, ~2026-07-18) #1012

Description

@izzoa

Describe the bug

On or around 2026-07-18 (first observed ~17:00 UTC on the US store; albums still worked a couple of hours earlier, so it may be rolling out gradually), Qobuz changed what their API returns:

  • rip url <any Qobuz album URL> now crashes with KeyError: 'tracks'
  • rip url <any Qobuz playlist URL> resolves 0 tracks and exits 0 without downloading anything

This is not specific to an account, region-locked release, or streamrip version: it reproduces on both v2.1.0 and current dev (the Qobuz client is identical in both), with a paid Qobuz Studio subscription, against every album tested including current editorial content.

Traceback

  File ".../streamrip/media/album.py", line 83, in resolve
    tracklist = get_album_track_ids(self.client.source, resp)
  File ".../streamrip/metadata/util.py", line 6, in get_album_track_ids
    tracklist = resp["tracks"]
                ~~~~^^^^^^^^^^
KeyError: 'tracks'

Root cause (verified with direct authenticated API calls, same app_id streamrip spoofs)

Qobuz removed inline track listings from the metadata endpoints:

Request Result
album/get?album_id=X 200, full album metadata, no tracks key at all
album/get?...&extra=tracks HTTP 400 — the old extra value is now rejected
album/get?...&extra=track_ids 200, new track_ids: [...] field with the complete id list
playlist/get?...&extra=tracks 200, but tracks.items is always [] (while tracks_count still reports the real count)
playlist/get?...&extra=track_ids 200, complete track_ids list
track/get?track_id=N unchanged — full track metadata
artist/get?...&extra=albums unchanged

The current Qobuz web player (bundle 8.2.0-b034) confirms the new contract: its album/playlist requests build extra=track_ids and hydrate track metadata separately (including via a new track/getList batch endpoint).

Reproduction (metadata stage only, no download)

import asyncio
from streamrip.config import Config, DEFAULT_CONFIG_PATH
from streamrip.client.qobuz import QobuzClient
from streamrip.metadata.util import get_album_track_ids

ALBUM_ID = "se9efo81r0qy2"  # any Qobuz album id reproduces this

async def main():
    client = QobuzClient(Config(DEFAULT_CONFIG_PATH))
    await client.login()
    try:
        resp = await client.get_metadata(ALBUM_ID, "album")
        print("'tracks' in album/get response:", "tracks" in resp)  # False
        print(get_album_track_ids("qobuz", resp))  # KeyError: 'tracks'
    finally:
        await client.session.close()

asyncio.run(main())

Suggested fix

streamrip already hydrates tracks one-by-one via track/get (PendingTrack / PendingPlaylistTrack), so it only ever needed the id lists from album/get / playlist/get. Requesting extra=track_ids and reading resp["track_ids"] when the legacy shape is absent fixes both flows with no new requests. I have this working — patch in the linked PR, verified with a full hi-res album download (10/10 tracks) and playlist resolution (66/66 pending tracks, previously 0).

Environment

  • streamrip v2.1.0 and dev (2.2.0) — both affected
  • Python 3.13.13, Linux (Unraid 7.3)
  • Qobuz Studio subscription, US store

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions