{"record":{"id":"4e2cbca4aa0bc511","repo":"PrefectHQ/fastmcp","slug":"failed-to-extract-key-id-from-token-e","errorCode":null,"errorMessage":"Failed to extract key ID from token: {e}","messagePattern":"Failed to extract key ID from token: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/cimd.py","lineNumber":673,"sourceCode":"\n        Args:\n            token: JWT token to extract kid from\n            jwks: JWKS document containing keys\n\n        Returns:\n            PEM-encoded public key\n\n        Raises:\n            ValueError: If key cannot be found or extracted\n        \"\"\"\n        # Extract kid from token header\n        try:\n            header_b64 = token.split(\".\")[0]\n            header_b64 += \"=\" * (4 - len(header_b64) % 4)  # Add padding\n            header = json.loads(base64.urlsafe_b64decode(header_b64))\n            kid = header.get(\"kid\")\n        except (IndexError, ValueError, json.JSONDecodeError) as e:\n            raise ValueError(f\"Failed to extract key ID from token: {e}\") from e\n\n        # Find matching key in JWKS\n        keys = jwks.get(\"keys\", [])\n        if not keys:\n            raise ValueError(\"JWKS document contains no keys\")\n\n        matching_key = None\n        for key in keys:\n            if kid and key.get(\"kid\") == kid:\n                matching_key = key\n                break\n\n        if not matching_key:\n            # If no kid match, try first key as fallback\n            if len(keys) == 1:\n                matching_key = keys[0]\n                self.logger.warning(\n                    \"No matching kid in JWKS, using single available key\"","sourceCodeStart":655,"sourceCodeEnd":691,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/cimd.py#L655-L691","documentation":"Raised by _extract_public_key_from_jwks when the token's JOSE header cannot be decoded to obtain the 'kid' (key ID). The method base64-decodes and JSON-parses the first dot-separated segment; malformed segments, non-JSON payloads, or structural problems raise this wrapped ValueError.","triggerScenarios":"Passing a token that is not a well-formed three-part JWT (missing dots, garbage prefix); a JWT header that is not valid base64url/JSON; passing a JWE (encrypted token) whose header is not a plain JOSE header; truncation of the token in transit.","commonSituations":"Debugging with placeholder/opaque tokens instead of real JWTs; clients accidentally sending access tokens (opaque) where a client assertion is required; newline/whitespace corruption of tokens copied from logs or config files.","solutions":["Verify the token has three dot-separated base64url segments and a JSON header containing 'kid'","Confirm you are passing the signed client assertion JWT, not an opaque access token","Log the first segment (header) and decode it manually to spot corruption/encoding issues"],"exampleFix":"// before\nkey = validator._extract_public_key_from_jwks(access_token, jwks)  # opaque token\n// after\nkey = validator._extract_public_key_from_jwks(client_assertion_jwt, jwks)  # real JWT with kid","handlingStrategy":"try-catch","validationCode":"import base64, json\ndef token_has_decodable_kid(token: str) -> bool:\n    try:\n        h = token.split(\".\")[0]\n        h += \"=\" * (4 - len(h) % 4)\n        return \"kid\" in json.loads(base64.urlsafe_b64decode(h))\n    except Exception:\n        return False","typeGuard":"def is_well_formed_jwt(token: str) -> bool:\n    parts = token.split(\".\")\n    if len(parts) != 3:\n        return False\n    try:\n        h = parts[0] + \"=\" * (4 - len(parts[0]) % 4)\n        json.loads(base64.urlsafe_b64decode(h))\n        return True\n    except Exception:\n        return False","tryCatchPattern":"try:\n    key = extract_public_key_from_jwks(token, jwks)\nexcept ValueError as e:\n    if \"Failed to extract key ID\" in str(e):\n        raise AssertionError(f\"token is not a decodable JWT: {token[:20]}...\") from e\n    raise","preventionTips":["Confirm tokens are three-segment base64url JWTs before use","Pass the signed client assertion, not an opaque access token","Strip whitespace/newlines when copying tokens from config or logs","Include a 'kid' header that matches a key in your JWKS"],"tags":["jwt","jwks","parsing","base64"],"backgroundTag":"malformed-jwt-token","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}