MCP OAuth invalid_grant: four causes at Sume's token endpoint

The Sume MCP token endpoint answers invalid_grant with one of five messages. They group into four causes: reuse, expiry, mismatch and a bad PKCE verifier.

5 min readSume
All posts

invalid_grant from Sume's hosted MCP token endpoint means the authorization code or the PKCE verifier was rejected, and the message text tells you which. The endpoint answers HTTP 400 with one of these messages: "Authorization code is invalid.", "Authorization code has expired.", "Authorization code does not match this token request.", "Invalid PKCE verifier.", or "Authorization code is already consumed." Most clients hide the text, so turn on the client's OAuth debug log first, then match the message against the list below.

The flow is the one in the MCP OAuth and API keys page: the client connects to https://mcp.sume.com/mcp, sends you to https://mcp.sume.com/oauth/authorize, you approve on the consent page, and the client exchanges the code (with PKCE) for an access token. The token step is the only place invalid_grant appears.

The messages, in the order the server checks them

Each message maps to one check that runs in a fixed order. The code is looked up by a hash; the server never keeps the raw code.

Token-endpoint invalid_grant messages in the Sume source (read 2026-10-05)
MessageWhat failedWhat to change
Authorization code is invalid.No such code, or the code was denied or already usedStart a new authorize request
Authorization code has expired.The code is older than its lifetime of 10 minutesExchange the code right after consent
Authorization code does not match this token request.client_id, redirect_uri or resource differs from the authorize stepSend the same three values in both calls
Invalid PKCE verifier.SHA-256 of the verifier does not equal the stored challengeReuse the verifier that made the challenge
Authorization code is already consumed.A concurrent exchange won the single-use raceDo not retry the same code; authorize again

Timing bugs and mismatch bugs

Two of these are timing bugs. The code lives 10 minutes and works once. A client that retries a failed token request with the same code can see the "invalid" or "already consumed" message on the second try, even though the first try failed for a different reason, such as a network drop after the server already consumed the code. The fix is never to loop on the token call. Treat any invalid_grant as the end of that authorization attempt and start a new one.

Two more are mismatch bugs. The code is bound to the client_id, the redirect_uri and the resource it was issued for. If your client sends https://mcp.sume.com/mcp as the resource at authorize time and omits it, or changes it, at token time, the code is rejected as not matching. The redirect URI must also be byte-for-byte the one from the authorize request.

Check your verifier and challenge pair

The PKCE check is the one that surprises people who build their own client. The server computes the S256 challenge from the code_verifier you send and compares it with the challenge saved when the code was issued. A verifier regenerated between the two steps, for example by a stateless serverless function, fails every time.

Keep the verifier next to the state value for the whole round trip. A short Python check of your own pair before you debug anything else:

import base64, hashlib, secrets

verifier = secrets.token_urlsafe(64)
digest = hashlib.sha256(verifier.encode()).digest()
challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
print("code_verifier :", verifier)
print("code_challenge:", challenge)
print("method        : S256")

A short triage list

Work through this list when a connector fails at the last step of sign-in:

  • Read the exact message, not just the error code.
  • Confirm the client sends the same client_id, redirect_uri and resource at both steps.
  • Confirm the verifier is the one that produced the challenge.
  • Make sure only one process exchanges the code, and that it does so soon after consent.
  • After any invalid_grant, restart the authorize step instead of retrying the token call.

When the token works but the tool call does not

If the sign-in succeeds but the next call is rejected, the problem has moved: the access token is valid for one hour, and a read-only grant cannot call tools that change data. Those cases are covered in MCP tools and gates. For the connect steps for Claude Code, Cursor and Codex, use the MCP quickstart.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume