Skip to content

JSONDecodeError raised instead of a HTTP error #928

Description

@mtricht

Describe the bug

When the token endpoint returns a non-JSON body with an HTTP status code below 500, OAuth2Client.fetch_token() raises a json.decoder.JSONDecodeError instead of a meaningful error that can be retried.

This happens because parse_response_token only calls resp.raise_for_status() when resp.status_code >= 500.

For any status code below 500 this check is skipped and if the response body isn't valid JSON, the code falls straight into resp.json() and raises with an unhandled JSONDecodeError. 429 is the case I hit in practice (auth servers/gateways send plain-text or HTML bodies on rate-limit responses), but the underlying issue applies to any non-5xx status paired with a non-JSON body.

Error Stacks

Traceback (most recent call last):
  File "/mnt/c/Users/Michael/Downloads/test.py", line 8, in <module>
    token = client.fetch_token(
        url=token_endpoint,
        grant_type="client_credentials",
    )
  File "/home/michael/.local/lib/python3.13/site-packages/authlib/oauth2/client.py", line 246, in fetch_token
    return self._fetch_token(
           ~~~~~~~~~~~~~~~~~^
        url, body=body, auth=auth, method=method, headers=headers, **session_kwargs
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/michael/.local/lib/python3.13/site-packages/authlib/oauth2/client.py", line 445, in _fetch_token
    return self.parse_response_token(resp)
           ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/home/michael/.local/lib/python3.13/site-packages/authlib/oauth2/client.py", line 418, in parse_response_token
    token = resp.json()
  File "/home/michael/.local/lib/python3.13/site-packages/httpx/_models.py", line 832, in json
    return jsonlib.loads(self.content, **kwargs)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "/usr/lib/python3.13/json/decoder.py", line 348, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 5 (char 4)

To Reproduce

server.py:

from http.server import HTTPServer, BaseHTTPRequestHandler

class TooManyRequestsHandler(BaseHTTPRequestHandler):
    def handle_request(self):
        self.send_response(429)
        self.end_headers()
        self.wfile.write(b"429 Too Many Requests")

    def do_GET(self):
        self.handle_request()

    def do_POST(self):
        self.handle_request()

    def do_PUT(self):
        self.handle_request()

    def do_DELETE(self):
        self.handle_request()

    def do_PATCH(self):
        self.handle_request()

if __name__ == "__main__":
    server = HTTPServer(("localhost", 8000), TooManyRequestsHandler)
    print("Server running on http://localhost:8000 (all routes return 429)")
    server.serve_forever()

test.py:

from authlib.integrations.httpx_client import OAuth2Client

client_id = "your_client_id"
client_secret = "your_client_secret"
token_endpoint = "http://localhost:8000"

with OAuth2Client(client_id, client_secret) as client:
    token = client.fetch_token(
        url=token_endpoint,
        grant_type="client_credentials",
    )
    print(token)

Expected behavior

A clear, catchable error is raised, either httpx.HTTPStatusError via resp.raise_for_status() or an authlib specific error.

Environment:

  • OS: any
  • Python Version: 3.13.5
  • Authlib Version: 1.7.2 but any will do

Additional context

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

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions