[BUG] MCP OAuth refresh token being used 4 days after it was already refreshed

Resolved 💬 2 comments Opened Jun 8, 2026 by jerbob92 Closed Jul 14, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Sometimes I lose authorization with our MCP server, I have added extra debug information in our MCP server and I see that the token was successfully refreshed at 2026-06-04T10:02:05Z, and that Claude then tries to refresh the exact same token today, at 2026-06-08T07:19:31Z. So it looks like either it's not storing the refresh token that it received from the first refresh attempt (FastMCP creates a new refresh token every time), or something else is wrong with the OAuth token refresh logic.

What Should Happen?

When a token is refreshed, it should store the new refresh token when the new token contains one. It should not attempt to refresh the same token twice.

Error Messages/Logs

FastMCP server logs:

INFO:     10.201.28.1:49042 - "GET /.well-known/oauth-authorization-server HTTP/1.1" 200 OK
DEBUG:mcp_gateway.main:POST /token request: dict_items([('grant_type', ['refresh_token']), ('refresh_token', ['{my-refresh-token}']), ('resource', ['https://{url}/mcp']), ('client_id', ['https://claude.ai/oauth/claude-code-client-metadata'])])
DEBUG:python_multipart.multipart:Calling on_field_start with no data
DEBUG:python_multipart.multipart:Calling on_field_name with data[0:10]
DEBUG:python_multipart.multipart:Calling on_field_data with data[11:24]
DEBUG:python_multipart.multipart:Calling on_field_end with no data
DEBUG:python_multipart.multipart:Calling on_field_start with no data
DEBUG:python_multipart.multipart:Calling on_field_name with data[25:38]
DEBUG:python_multipart.multipart:Calling on_field_data with data[39:1406]
DEBUG:python_multipart.multipart:Calling on_field_end with no data
DEBUG:python_multipart.multipart:Calling on_field_start with no data
DEBUG:python_multipart.multipart:Calling on_field_name with data[1407:1415]
DEBUG:python_multipart.multipart:Calling on_field_data with data[1416:1462]
DEBUG:python_multipart.multipart:Calling on_field_end with no data
DEBUG:python_multipart.multipart:Calling on_field_start with no data
DEBUG:python_multipart.multipart:Calling on_field_name with data[1463:1472]
DEBUG:python_multipart.multipart:Calling on_field_data with data[1473:1534]
DEBUG:python_multipart.multipart:Calling on_field_end with no data
DEBUG:python_multipart.multipart:Calling on_end with no data
[06/08/26 07:19:31] DEBUG    SSRF-safe fetch:                        ssrf.py:272
                             https://claude.ai/oauth/claude-code-cli
                             ent-metadata ->
                             https://[2607:6bc0::10]:443/oauth/claud
                             e-code-client-metadata (pinned to
                             2607:6bc0::10)
DEBUG:httpcore.connection:connect_tcp.started host='2607:6bc0::10' port=443 local_address=None timeout=10.0 socket_options=None
DEBUG:httpcore.connection:connect_tcp.failed exception=ConnectError(OSError('All connection attempts failed'))
                    DEBUG    SSRF-safe fetch:                        ssrf.py:272
                             https://claude.ai/oauth/claude-code-cli
                             ent-metadata ->
                             https://160.79.104.10:443/oauth/claude-
                             code-client-metadata (pinned to
                             160.79.104.10)
DEBUG:httpcore.connection:connect_tcp.started host='160.79.104.10' port=443 local_address=None timeout=10.0 socket_options=None
DEBUG:httpcore.connection:connect_tcp.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x78ece662a030>
DEBUG:httpcore.connection:start_tls.started ssl_context=<ssl.SSLContext object at 0x78ece676e3f0> server_hostname='claude.ai' timeout=10.0
DEBUG:httpcore.connection:start_tls.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x78ece67c8050>
DEBUG:httpcore.http11:send_request_headers.started request=<Request [b'GET']>
DEBUG:httpcore.http11:send_request_headers.complete
DEBUG:httpcore.http11:send_request_body.started request=<Request [b'GET']>
DEBUG:httpcore.http11:send_request_body.complete
DEBUG:httpcore.http11:receive_response_headers.started request=<Request [b'GET']>
DEBUG:httpcore.http11:receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Date', b'Mon, 08 Jun 2026 07:19:32 GMT'), (b'Content-Type', b'application/json'), (b'Transfer-Encoding', b'chunked'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'public, max-age=300'), (b'request-id', b'req_011CbqPNqfGg8rjNVYMegUgh'), (b'strict-transport-security', b'max-age=31536000; includeSubDomains; preload'), (b'Server', b'cloudflare'), (b'vary', b'Accept-Encoding'), (b'cf-cache-status', b'DYNAMIC'), (b'set-cookie', b'__cf_bm=xxxx; HttpOnly; SameSite=None; Secure; Path=/; Domain=claude.ai; Expires=Mon, 08 Jun 2026 07:49:32 GMT'), (b'set-cookie', b'_cfuvid=xxxx; HttpOnly; SameSite=None; Secure; Path=/; Domain=claude.ai'), (b'X-Robots-Tag', b'none'), (b'Content-Encoding', b'zstd'), (b'CF-RAY', b'xxxx'), (b'alt-svc', b'h3=":443"; ma=86400')])
INFO:httpx:HTTP Request: GET https://160.79.104.10/oauth/claude-code-client-metadata "HTTP/1.1 200 OK"
DEBUG:httpcore.http11:receive_response_body.started request=<Request [b'GET']>
DEBUG:httpcore.http11:receive_response_body.complete
DEBUG:httpcore.http11:response_closed.started
DEBUG:httpcore.http11:response_closed.complete
DEBUG:httpcore.connection:close.started
DEBUG:httpcore.connection:close.complete
[06/08/26 07:19:32] INFO     CIMD document fetched and validated:    cimd.py:402
                             https://claude.ai/oauth/claude-code-cli
                             ent-metadata (client_name=Claude Code)
                    DEBUG    CIMD client resolved:                   cimd.py:762
                             https://claude.ai/oauth/claude-code-cli
                             ent-metadata (name=Claude Code)
                    DEBUG    CIMD client resolved:                   cimd.py:762
                             https://claude.ai/oauth/claude-code-cli
                             ent-metadata (name=Claude Code)
INFO:     10.201.28.1:49056 - "POST /token HTTP/1.1" 401 Unauthorized
WARNING:mcp_gateway.main:POST /token failed (status=401) fields=dict_items([('grant_type', ['refresh_token']), ('refresh_token', ['{my-refresh-token}']), ('resource', ['https://{url}/mcp']), ('client_id', ['https://claude.ai/oauth/claude-code-client-metadata'])]) error={"error":"invalid_grant","error_description":"refresh token does not exist"}

Claude was not running in debug mode when this happened, and I can't reproduce this on demand. I will try to run in debug mode next time.

Steps to Reproduce

I can't reproduce this on demand sadly.

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

v2.1.168

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

This has been reported at FastMCP as well: https://github.com/PrefectHQ/fastmcp/issues/4265

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗