Skip to content

[BUG] <title>Tidal device authorization Errors/MAC OS #985

Description

@iamhongliang

Describe the bug

Hi ,
When I tried to download url ,it always show the following errors :

hongliang@MacBook-Pro-6 bin % rip --quality 3 url https://tidal.com/browse/album/147569387
Go to https://link.tidal.com/VEEWS to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ContentTypeError: 403, message='Attempt to decode JSON with unexpected mimetype: text/html', url='https://auth.tidal.com/v1/oauth2/token'
hongliang@MacBook-Pro-6 bin % rip --quality 3 url https://tidal.com/browse/album/147569387
Go to https://link.tidal.com/WNVBO to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ContentTypeError: 403, message='Attempt to decode JSON with unexpected mimetype: text/html', url='https://auth.tidal.com/v1/oauth2/token'
hongliang@MacBook-Pro-6 bin % rip url https://tidal.com/album/461388201
Go to https://link.tidal.com/CZLMV to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │

Command Used

rip url https://tidal.com/album/461388201

Debug Traceback

hongliang@MacBook-Pro-6 bin % rip -vvv url https://tidal.com/album/461388201          
[10:05:02] DEBUG    Showing all debug logs                                                                                                                                                                 cli.py:111
Go to https://link.tidal.com/BFONT to log into Tidal within 5 minutes.
           DEBUG    Checking with {'client_id': 'zU4XHVVkc2tDPo4t', 'device_code': '16cd7add-4c2f-46c8-96dc-3e3e13f20c24', 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code', 'scope':        tidal.py:271
                    'r_usr+w_usr+w_sub'}                                                                                                                                                                             
           DEBUG    Removing dirs set()                                                                                                                                                                 artwork.py:19
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in <module>                                                              │
│                                                                                                  │
│   3 from streamrip.rip import rip                                                                │
│   4 if __name__ == '__main__':                                                                   │
│   5 │   sys.argv[0] = sys.argv[0].removesuffix('.exe')                                           │
│ ❱ 6 │   sys.exit(rip())                                                                          │
│   7                                                                                              │
│                                                                                                  │
│ ╭──────────── locals ─────────────╮                                                              │
│ │ rip = <HelpColorsGroup rip>     │                                                              │
│ │ sys = <module 'sys' (built-in)> │                                                              │
│ ╰─────────────────────────────────╯                                                              │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in __call__                        │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1445 in main                            │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1912 in invoke                          │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1308 in invoke                          │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:877 in invoke                           │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/click/decorators.py:34 in new_func                    │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:29 in wrapper                    │
│                                                                                                  │
│    26 def coro(f):                                                                               │
│    27 │   @wraps(f)                                                                              │
│    28 │   def wrapper(*args, **kwargs):                                                          │
│ ❱  29 │   │   return asyncio.run(f(*args, **kwargs))                                             │
│    30 │                                                                                          │
│    31 │   return wrapper                                                                         │
│    32                                                                                            │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │   args = (<click.core.Context object at 0x10ada9950>,)    │                                    │
│ │ kwargs = {'urls': ('https://tidal.com/album/461388201',)} │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /opt/homebrew/Cellar/python@3.14/3.14.5/Frameworks/Python.framework/Versions/3.14/lib/python3.14 │
│ /asyncio/runners.py:204 in run                                                                   │
│                                                                                                  │
│   201 │   │   │   "asyncio.run() cannot be called from a running event loop")                    │
│   202 │                                                                                          │
│   203 │   with Runner(debug=debug, loop_factory=loop_factory) as runner:                         │
│ ❱ 204 │   │   return runner.run(main)                                                            │
│   205                                                                                            │
│   206                                                                                            │
│   207 def _cancel_all_tasks(loop):                                                               │
│                                                                                                  │
│ ╭─────────────────────────── locals ────────────────────────────╮                                │
│ │        debug = None                                           │                                │
│ │ loop_factory = None                                           │                                │
│ │         main = <coroutine object url at 0x10ace3ca0>          │                                │
│ │       runner = <asyncio.runners.Runner object at 0x10ace3e00> │                                │
│ ╰───────────────────────────────────────────────────────────────╯                                │
│                                                                                                  │
│ /opt/homebrew/Cellar/python@3.14/3.14.5/Frameworks/Python.framework/Versions/3.14/lib/python3.14 │
│ /asyncio/runners.py:127 in run                                                                   │
│                                                                                                  │
│   124 │   │                                                                                      │
│   125 │   │   self._interrupt_count = 0                                                          │
│   126 │   │   try:                                                                               │
│ ❱ 127 │   │   │   return self._loop.run_until_complete(task)                                     │
│   128 │   │   except exceptions.CancelledError:                                                  │
│   129 │   │   │   if self._interrupt_count > 0:                                                  │
│   130 │   │   │   │   uncancel = getattr(task, "uncancel", None)                                 │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │        context = <_contextvars.Context object at 0x10a83cfc0>                                │ │
│ │           coro = <coroutine object url at 0x10ace3ca0>                                       │ │
│ │           self = <asyncio.runners.Runner object at 0x10ace3e00>                              │ │
│ │ sigint_handler = functools.partial(<bound method Runner._on_sigint of                        │ │
│ │                  <asyncio.runners.Runner object at 0x10ace3e00>>, main_task=<Task finished   │ │
│ │                  name='Task-1' coro=<url() done, defined at                                  │ │
│ │                  /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:167>        │ │
│ │                  exception=ContentTypeError(RequestInfo(url=URL('https://auth.tidal.com/v1/… │ │
│ │                  method='POST', headers=<CIMultiDictProxy('Host': 'auth.tidal.com',          │ │
│ │                  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0)           │ │
│ │                  Gecko/20100101 Firefox/83.0', 'Accept': '*/*', 'Accept-Encoding': 'gzip,    │ │
│ │                  deflate, br, zstd', 'Authorization': 'Basic                                 │ │
│ │                  elU0WEhWVmtjMnREUG80dDpWSktoREZxSlBxdnNQVk5CVjZ1a1hUSm13bHZidHRQN3dsTWxyYz… │ │
│ │                  'Content-Length': '169', 'Content-Type':                                    │ │
│ │                  'application/x-www-form-urlencoded')>,                                      │ │
│ │                  real_url=URL('https://auth.tidal.com/v1/oauth2/token')), (), status=403,    │ │
│ │                  message='Attempt to decode JSON with unexpected mimetype: text/html',       │ │
│ │                  headers=<CIMultiDictProxy('Server': 'CloudFront', 'Date': 'Thu, 11 Jun 2026 │ │
│ │                  02:05:03 GMT', 'Content-Type': 'text/html', 'Content-Length': '919',        │ │
│ │                  'Connection': 'keep-alive', 'X-Cache': 'Error from cloudfront', 'Via': '1.1 │ │
│ │                  d17c184839dfdf966b08e3b020308166.cloudfront.net (CloudFront)',              │ │
│ │                  'X-Amz-Cf-Pop': 'SFO53-P12', 'X-Amz-Cf-Id':                                 │ │
│ │                  'JzSPtjuZoOD9r079I9goDncj1AuS0m6Jc3FNYI9iX-_XXc_t1Pb8cg==')>)>)             │ │
│ │           task = <Task finished name='Task-1' coro=<url() done, defined at                   │ │
│ │                  /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:167>        │ │
│ │                  exception=ContentTypeError(RequestInfo(url=URL('https://auth.tidal.com/v1/… │ │
│ │                  method='POST', headers=<CIMultiDictProxy('Host': 'auth.tidal.com',          │ │
│ │                  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0)           │ │
│ │                  Gecko/20100101 Firefox/83.0', 'Accept': '*/*', 'Accept-Encoding': 'gzip,    │ │
│ │                  deflate, br, zstd', 'Authorization': 'Basic                                 │ │
│ │                  elU0WEhWVmtjMnREUG80dDpWSktoREZxSlBxdnNQVk5CVjZ1a1hUSm13bHZidHRQN3dsTWxyYz… │ │
│ │                  'Content-Length': '169', 'Content-Type':                                    │ │
│ │                  'application/x-www-form-urlencoded')>,                                      │ │
│ │                  real_url=URL('https://auth.tidal.com/v1/oauth2/token')), (), status=403,    │ │
│ │                  message='Attempt to decode JSON with unexpected mimetype: text/html',       │ │
│ │                  headers=<CIMultiDictProxy('Server': 'CloudFront', 'Date': 'Thu, 11 Jun 2026 │ │
│ │                  02:05:03 GMT', 'Content-Type': 'text/html', 'Content-Length': '919',        │ │
│ │                  'Connection': 'keep-alive', 'X-Cache': 'Error from cloudfront', 'Via': '1.1 │ │
│ │                  d17c184839dfdf966b08e3b020308166.cloudfront.net (CloudFront)',              │ │
│ │                  'X-Amz-Cf-Pop': 'SFO53-P12', 'X-Amz-Cf-Id':                                 │ │
│ │                  'JzSPtjuZoOD9r079I9goDncj1AuS0m6Jc3FNYI9iX-_XXc_t1Pb8cg==')>)>              │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /opt/homebrew/Cellar/python@3.14/3.14.5/Frameworks/Python.framework/Versions/3.14/lib/python3.14 │
│ /asyncio/base_events.py:719 in run_until_complete                                                │
│                                                                                                  │
│    716 │   │   if not future.done():                                                             │
│    717 │   │   │   raise RuntimeError('Event loop stopped before Future completed.')             │
│    718 │   │                                                                                     │
│ ❱  719 │   │   return future.result()                                                            │
│    720 │                                                                                         │
│    721 │   def stop(self):                                                                       │
│    722 │   │   """Stop running the event loop.                                                   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   future = <Task finished name='Task-1' coro=<url() done, defined at                         │ │
│ │            /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:167>              │ │
│ │            exception=ContentTypeError(RequestInfo(url=URL('https://auth.tidal.com/v1/oauth2… │ │
│ │            method='POST', headers=<CIMultiDictProxy('Host': 'auth.tidal.com', 'User-Agent':  │ │
│ │            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0', │ │
│ │            'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Authorization':   │ │
│ │            'Basic                                                                            │ │
│ │            elU0WEhWVmtjMnREUG80dDpWSktoREZxSlBxdnNQVk5CVjZ1a1hUSm13bHZidHRQN3dsTWxyYzcyc2U0… │ │
│ │            'Content-Length': '169', 'Content-Type': 'application/x-www-form-urlencoded')>,   │ │
│ │            real_url=URL('https://auth.tidal.com/v1/oauth2/token')), (), status=403,          │ │
│ │            message='Attempt to decode JSON with unexpected mimetype: text/html',             │ │
│ │            headers=<CIMultiDictProxy('Server': 'CloudFront', 'Date': 'Thu, 11 Jun 2026       │ │
│ │            02:05:03 GMT', 'Content-Type': 'text/html', 'Content-Length': '919',              │ │
│ │            'Connection': 'keep-alive', 'X-Cache': 'Error from cloudfront', 'Via': '1.1       │ │
│ │            d17c184839dfdf966b08e3b020308166.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop':    │ │
│ │            'SFO53-P12', 'X-Amz-Cf-Id':                                                       │ │
│ │            'JzSPtjuZoOD9r079I9goDncj1AuS0m6Jc3FNYI9iX-_XXc_t1Pb8cg==')>)>                    │ │
│ │ new_task = False                                                                             │ │
│ │     self = <_UnixSelectorEventLoop running=False closed=True debug=False>                    │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:191 in url                       │
│                                                                                                  │
│   188 │   │   │   │   version_coro = None                                                        │
│   189 │   │   │                                                                                  │
│   190 │   │   │   async with Main(cfg) as main:                                                  │
│ ❱ 191 │   │   │   │   await main.add_all(urls)                                                   │
│   192 │   │   │   │   await main.resolve()                                                       │
│   193 │   │   │   │   await main.rip()                                                           │
│   194                                                                                            │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │          cfg = <streamrip.config.Config object at 0x10ace2900>                               │ │
│ │          ctx = <click.core.Context object at 0x10ada9950>                                    │ │
│ │         main = <streamrip.rip.main.Main object at 0x10aef46e0>                               │ │
│ │      updates = True                                                                          │ │
│ │         urls = ('https://tidal.com/album/461388201',)                                        │ │
│ │ version_coro = <Task finished name='Task-2' coro=<latest_streamrip_version() done, defined   │ │
│ │                at /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/cli.py:451>       │ │
│ │                result=('2.1.0', None)>                                                       │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/main.py:124 in add_all                  │
│                                                                                                  │
│   121 │   │   │   │   │   f"[red]Found invalid url [cyan]{urls[i]}[/cyan], skipping.",           │
│   122 │   │   │   │   )                                                                          │
│   123 │   │   │   │   continue                                                                   │
│ ❱ 124 │   │   │   url_client_pairs.append((p, await self.get_logged_in_client(p.source)))        │
│   125 │   │                                                                                      │
│   126 │   │   pendings = await asyncio.gather(                                                   │
│   127 │   │   │   *[                                                                             │
│                                                                                                  │
│ ╭──────────────────────────────────── locals ─────────────────────────────────────╮              │
│ │                i = 0                                                            │              │
│ │                p = <streamrip.rip.parse_url.GenericURL object at 0x10aef5be0>   │              │
│ │           parsed = [<streamrip.rip.parse_url.GenericURL object at 0x10aef5be0>] │              │
│ │             self = <streamrip.rip.main.Main object at 0x10aef46e0>              │              │
│ │ url_client_pairs = []                                                           │              │
│ │             urls = ('https://tidal.com/album/461388201',)                       │              │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯              │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/main.py:145 in get_logged_in_client     │
│                                                                                                  │
│   142 │   │   │   prompter = get_prompter(client, self.config)                                   │
│   143 │   │   │   if not prompter.has_creds():                                                   │
│   144 │   │   │   │   # Get credentials from user and log into client                            │
│ ❱ 145 │   │   │   │   await prompter.prompt_and_login()                                          │
│   146 │   │   │   │   prompter.save()                                                            │
│   147 │   │   │   else:                                                                          │
│   148 │   │   │   │   with console.status(f"[cyan]Logging into {source}", spinner="dots"):       │
│                                                                                                  │
│ ╭──────────────────────────────── locals ─────────────────────────────────╮                      │
│ │   client = <streamrip.client.tidal.TidalClient object at 0x10aef4980>   │                      │
│ │ prompter = <streamrip.rip.prompter.TidalPrompter object at 0x10aef5d30> │                      │
│ │     self = <streamrip.rip.main.Main object at 0x10aef46e0>              │                      │
│ │   source = 'tidal'                                                      │                      │
│ ╰─────────────────────────────────────────────────────────────────────────╯                      │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/rip/prompter.py:114 in prompt_and_login     │
│                                                                                                  │
│   111 │   │   info = {}                                                                          │
│   112 │   │   while elapsed < self.timeout_s:                                                    │
│   113 │   │   │   elapsed = time.time() - start                                                  │
│ ❱ 114 │   │   │   status, info = await self.client._get_auth_status(device_code)                 │
│   115 │   │   │   if status == 2:                                                                │
│   116 │   │   │   │   # pending                                                                  │
│   117 │   │   │   │   await asyncio.sleep(4)                                                     │
│                                                                                                  │
│ ╭────────────────────────────────── locals ──────────────────────────────────╮                   │
│ │ device_code = '16cd7add-4c2f-46c8-96dc-3e3e13f20c24'                       │                   │
│ │     elapsed = 5.245208740234375e-06                                        │                   │
│ │        info = {}                                                           │                   │
│ │  login_link = 'https://link.tidal.com/BFONT'                               │                   │
│ │        self = <streamrip.rip.prompter.TidalPrompter object at 0x10aef5d30> │                   │
│ │       start = 1781143502.804384                                            │                   │
│ │         uri = 'link.tidal.com/BFONT'                                       │                   │
│ ╰────────────────────────────────────────────────────────────────────────────╯                   │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:272 in _get_auth_status     │
│                                                                                                  │
│   269 │   │   │   "scope": "r_usr+w_usr+w_sub",                                                  │
│   270 │   │   }                                                                                  │
│   271 │   │   logger.debug("Checking with %s", data)                                             │
│ ❱ 272 │   │   resp = await self._api_post(f"{AUTH_URL}/token", data, AUTH)                       │
│   273 │   │                                                                                      │
│   274 │   │   if "status" in resp and resp["status"] != 200:                                     │
│   275 │   │   │   if resp["status"] == 400 and resp["sub_status"] == 1002:                       │
│                                                                                                  │
│ ╭──────────────────────────────────── locals ─────────────────────────────────────╮              │
│ │        data = {                                                                 │              │
│ │               │   'client_id': 'zU4XHVVkc2tDPo4t',                              │              │
│ │               │   'device_code': '16cd7add-4c2f-46c8-96dc-3e3e13f20c24',        │              │
│ │               │   'grant_type': 'urn:ietf:params:oauth:grant-type:device_code', │              │
│ │               │   'scope': 'r_usr+w_usr+w_sub'                                  │              │
│ │               }                                                                 │              │
│ │ device_code = '16cd7add-4c2f-46c8-96dc-3e3e13f20c24'                            │              │
│ │        self = <streamrip.client.tidal.TidalClient object at 0x10aef4980>        │              │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯              │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post            │
│                                                                                                  │
│   334 │   │   """                                                                                │
│   335 │   │   async with self.rate_limiter:                                                      │
│   336 │   │   │   async with self.session.post(url, data=data, auth=auth) as resp:               │
│ ❱ 337 │   │   │   │   return await resp.json()                                                   │
│   338 │                                                                                          │
│   339 │   async def _api_request(self, path: str, params=None, base: str = BASE) -> dict:        │
│   340 │   │   """Handle Tidal API requests.                                                      │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ auth = BasicAuth(                                                                            │ │
│ │        │   login='zU4XHVVkc2tDPo4t',                                                         │ │
│ │        │   password='VJKhDFqJPqvsPVNBV6ukXTJmwlvbttP7wlMlrc72se4=',                          │ │
│ │        │   encoding='latin1'                                                                 │ │
│ │        )                                                                                     │ │
│ │ data = {                                                                                     │ │
│ │        │   'client_id': 'zU4XHVVkc2tDPo4t',                                                  │ │
│ │        │   'device_code': '16cd7add-4c2f-46c8-96dc-3e3e13f20c24',                            │ │
│ │        │   'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',                     │ │
│ │        │   'scope': 'r_usr+w_usr+w_sub'                                                      │ │
│ │        }                                                                                     │ │
│ │ resp = <ClientResponse(https://auth.tidal.com/v1/oauth2/token) [403 Forbidden]>              │ │
│ │        <CIMultiDictProxy('Server': 'CloudFront', 'Date': 'Thu, 11 Jun 2026 02:05:03 GMT',    │ │
│ │        'Content-Type': 'text/html', 'Content-Length': '919', 'Connection': 'keep-alive',     │ │
│ │        'X-Cache': 'Error from cloudfront', 'Via': '1.1                                       │ │
│ │        d17c184839dfdf966b08e3b020308166.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop':        │ │
│ │        'SFO53-P12', 'X-Amz-Cf-Id':                                                           │ │
│ │        'JzSPtjuZoOD9r079I9goDncj1AuS0m6Jc3FNYI9iX-_XXc_t1Pb8cg==')>                          │ │
│ │ self = <streamrip.client.tidal.TidalClient object at 0x10aef4980>                            │ │
│ │  url = 'https://auth.tidal.com/v1/oauth2/token'                                              │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json                  │
│                                                                                                  │
│    775 │   │   if content_type:                                                                  │
│    776 │   │   │   ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()                       │
│    777 │   │   │   if not _is_expected_content_type(ctype, content_type):                        │
│ ❱  778 │   │   │   │   raise ContentTypeError(                                                   │
│    779 │   │   │   │   │   self.request_info,                                                    │
│    780 │   │   │   │   │   self.history,                                                         │
│    781 │   │   │   │   │   status=self.status,                                                   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ content_type = 'application/json'                                                            │ │
│ │        ctype = 'text/html'                                                                   │ │
│ │     encoding = None                                                                          │ │
│ │         self = <ClientResponse(https://auth.tidal.com/v1/oauth2/token) [403 Forbidden]>      │ │
│ │                <CIMultiDictProxy('Server': 'CloudFront', 'Date': 'Thu, 11 Jun 2026 02:05:03  │ │
│ │                GMT', 'Content-Type': 'text/html', 'Content-Length': '919', 'Connection':     │ │
│ │                'keep-alive', 'X-Cache': 'Error from cloudfront', 'Via': '1.1                 │ │
│ │                d17c184839dfdf966b08e3b020308166.cloudfront.net (CloudFront)',                │ │
│ │                'X-Amz-Cf-Pop': 'SFO53-P12', 'X-Amz-Cf-Id':                                   │ │
│ │                'JzSPtjuZoOD9r079I9goDncj1AuS0m6Jc3FNYI9iX-_XXc_t1Pb8cg==')>                  │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ContentTypeError: 403, message='Attempt to decode JSON with unexpected mimetype: text/html', url='https://auth.tidal.com/v1/oauth2/token'

Config File

[tidal]
# 0: 256kbps AAC, 1: 320kbps AAC, 2: 16/44.1 "HiFi" FLAC, 3: 24/44.1 "MQA" FLAC
quality = 3
# This will download videos included in Video Albums.
download_videos = false

# Do not change any of the fields below
user_id = ""
country_code = ""
access_token = ""
refresh_token = ""
# Tokens last 1 week after refresh. This is the Unix timestamp of the expiration
# time. If you haven't used streamrip in more than a week, you may have to log
# in again using `rip config --tidal`
token_expiry = ""

Operating System

MacOS

streamrip version

rip, version 2.1.0

Screenshots and recordings

No response

Additional context

Hi ,
When I tried to download url ,it always show the following errors :

hongliang@MacBook-Pro-6 bin % rip --quality 3 url https://tidal.com/browse/album/147569387
Go to https://link.tidal.com/VEEWS to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ContentTypeError: 403, message='Attempt to decode JSON with unexpected mimetype: text/html', url='https://auth.tidal.com/v1/oauth2/token'
hongliang@MacBook-Pro-6 bin % rip --quality 3 url https://tidal.com/browse/album/147569387
Go to https://link.tidal.com/WNVBO to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ContentTypeError: 403, message='Attempt to decode JSON with unexpected mimetype: text/html', url='https://auth.tidal.com/v1/oauth2/token'
hongliang@MacBook-Pro-6 bin % rip url https://tidal.com/album/461388201
Go to https://link.tidal.com/CZLMV to log into Tidal within 5 minutes.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/homebrew/bin/rip:6 in │
│ │
│ 3 from streamrip.rip import rip │
│ 4 if name == 'main': │
│ 5 │ sys.argv[0] = sys.argv[0].removesuffix('.exe') │
│ ❱ 6 │ sys.exit(rip()) │
│ 7 │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/click/core.py:1524 in call │
│ │
│ ... 14 frames hidden ... │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/streamrip/client/tidal.py:337 in _api_post │
│ │
│ 334 │ │ """ │
│ 335 │ │ async with self.rate_limiter: │
│ 336 │ │ │ async with self.session.post(url, data=data, auth=auth) as resp: │
│ ❱ 337 │ │ │ │ return await resp.json() │
│ 338 │ │
│ 339 │ async def _api_request(self, path: str, params=None, base: str = BASE) -> dict: │
│ 340 │ │ """Handle Tidal API requests. │
│ │
│ /opt/homebrew/lib/python3.14/site-packages/aiohttp/client_reqrep.py:778 in json │
│ │
│ 775 │ │ if content_type: │
│ 776 │ │ │ ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() │
│ 777 │ │ │ if not _is_expected_content_type(ctype, content_type): │
│ ❱ 778 │ │ │ │ raise ContentTypeError( │
│ 779 │ │ │ │ │ self.request_info, │
│ 780 │ │ │ │ │ self.history, │
│ 781 │ │ │ │ │ status=self.status, │

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions