{"record":{"id":"b779535557891a65","repo":"PrefectHQ/fastmcp","slug":"token-type-mismatch-expected-expected-token-use","errorCode":null,"errorMessage":"Token type mismatch: expected {expected_token_use}, got {token_use}","messagePattern":"Token type mismatch: expected (.+?), got (.+?)","errorType":"validation","errorClass":"JoseError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/jwt_issuer.py","lineNumber":268,"sourceCode":"            JoseError: If token is invalid, expired, or has wrong claims\n        \"\"\"\n        try:\n            # Decode and verify signature\n            payload = jwt.decode(\n                token,\n                self._jwt_key,\n                algorithms=[\"HS256\"],\n            ).claims\n\n            # Validate token type\n            token_use = payload.get(\"token_use\", \"access\")\n            if token_use != expected_token_use:\n                logger.debug(\n                    \"Token type mismatch: expected %s, got %s\",\n                    expected_token_use,\n                    token_use,\n                )\n                raise JoseError(\n                    f\"Token type mismatch: expected {expected_token_use}, \"\n                    f\"got {token_use}\"\n                )\n\n            # Validate expiration\n            exp = payload.get(\"exp\")\n            if exp is not None and exp < time.time():\n                logger.debug(\"Token expired\")\n                raise JoseError(\"Token has expired\")\n\n            # Validate issuer\n            if payload.get(\"iss\") != self.issuer:\n                logger.debug(\"Token has invalid issuer\")\n                raise JoseError(\"Invalid token issuer\")\n\n            # Validate audience\n            if payload.get(\"aud\") != self.audience:\n                logger.debug(\"Token has invalid audience\")","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/jwt_issuer.py#L250-L286","documentation":"JWTIssuer.verify_token() distinguishes token purposes via the 'token_use' claim (e.g. access vs refresh/id). If the token decodes and signature-verifies but its token_use differs from the expected_token_use argument, it is rejected with JoseError — the token is cryptographically valid but is the wrong kind of token for this endpoint.","triggerScenarios":"Calling verify_token(token, expected_token_use='access') on a JWT whose payload token_use is 'refresh' (or vice versa), mismatch at jwt_issuer.py:268; also happens when the wrong client passes an ID token where an access token is expected.","commonSituations":"Client stores multiple tokens and sends the wrong one in the Authorization header; a token-minting bug sets token_use incorrectly; API surface changed to require a token_use claim the issuer never set; copy-pasting a refresh token into an access-token slot in tests.","solutions":["Send the token whose token_use matches what the endpoint expects — check which stored token you attach to the request","Fix the issuer/minting code to stamp token_use correctly for each token type","Ensure verify_token is called with the expected_token_use value that matches the tokens your issuer emits","If your flow doesn't use token-type separation, omit/align the token_use claim consistently between issuer and verifier"],"exampleFix":"// before\nheaders = {\"Authorization\": f\"Bearer {refresh_token}\"}  # wrong token\n// after\nheaders = {\"Authorization\": f\"Bearer {access_token}\"}","handlingStrategy":"try-catch","validationCode":"claims = jwt.decode(token, options={\"verify_signature\": False})\nif claims.get(\"token_use\") != \"access\":\n    raise ValueError(\"Wrong token selected; send the access token, not refresh/ID token\")","typeGuard":"def is_access_token(claims: dict) -> bool:\n    return claims.get(\"token_use\") == \"access\"","tryCatchPattern":"try:\n    payload = issuer.verify_token(token, expected_token_use=\"access\")\nexcept JoseError as e:\n    if \"Token type mismatch\" in str(e):\n        token = select_token_for_use(\"access\")\n        payload = issuer.verify_token(token, expected_token_use=\"access\")\n    else:\n        raise","preventionTips":["Store access and refresh tokens under distinct keys client-side","Stamp token_use correctly at mint time and test it","Never paste tokens across purposes in tests or docs examples"],"tags":["jwt","auth","token-validation"],"backgroundTag":"token-type-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}