{"record":{"id":"c891a64be2a07c55","repo":"langchain-ai/deepagents","slug":"token-response-from-token-url-is-not-a-valid-oau","errorCode":null,"errorMessage":"Token response from {token_url} is not a valid OAuth token payload: {exc}","messagePattern":"Token response from (.+?) is not a valid OAuth token payload: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/mcp_auth.py","lineNumber":1979,"sourceCode":"            if err:\n                msg = f\"Device flow failed: {err}: {body.get('error_description', '')}\"\n                raise RuntimeError(msg)\n            try:\n                token_response.raise_for_status()\n            except httpx.HTTPStatusError as exc:\n                msg = (\n                    f\"Token request failed: HTTP {token_response.status_code} \"\n                    f\"from {token_url}.\"\n                )\n                raise RuntimeError(msg) from exc\n            try:\n                return OAuthToken.model_validate(body)\n            except ValidationError as exc:\n                msg = (\n                    f\"Token response from {token_url} is not a valid \"\n                    f\"OAuth token payload: {exc}\"\n                )\n                raise RuntimeError(msg) from exc\n\n    msg = \"Device flow timed out. Try logging in again.\"\n    raise RuntimeError(msg)\n\n\ndef format_login_failure(exc: BaseException) -> str:\n    \"\"\"Return a token-safe single-line summary of an OAuth-login exception.\n\n    OAuth handshakes commonly surface as `ExceptionGroup` (anyio task\n    groups) or as MCP-SDK errors whose `args`/`repr` may include an\n    `OAuthToken`. Never call `str()`/`repr()` on the raw exception for\n    display or logging — instead, prefer a known-safe nested\n    `MCPReauthRequiredError` message, fall back to the messages of our\n    own loopback-related exception types, and degrade to a class-name\n    chain for anything else.\n\n    Args:\n        exc: Root exception caught from the login worker.","sourceCodeStart":1961,"sourceCodeEnd":1997,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/mcp_auth.py#L1961-L1997","documentation":"`_run_device_flow` raises this RuntimeError when the token endpoint responds HTTP 200 but the JSON body does not match the `OAuthToken` pydantic schema (missing/invalid `access_token`, `token_type`, or `expires_in` fields). This guards against APIs or proxies returning unexpected payloads.","triggerScenarios":"A successful-status response whose body lacks the required OAuth token fields — e.g. an authentication proxy returns an HTML login page with 200, or a base-URL override points at a server returning a different JSON shape.","commonSituations":"Captive portals or SSO gateways injecting HTML into responses; a mocked/stubbed GitHub-compatible endpoint returning partial fields; schema drift when pointing at a self-hosted or enterprise instance; an intermediary stripping response fields.","solutions":["Inspect the pydantic ValidationError detail in the message to see which field is missing or mistyped","Confirm you are hitting the real GitHub token endpoint and not a proxy/captive portal returning HTML","If using a custom base URL or enterprise instance, verify its token response matches the standard OAuth token schema","Update the package if GitHub's payload shape changed and a newer version handles it"],"exampleFix":"// before: custom endpoint returning non-OAuth JSON\n\"github_api_url\": \"https://internal-mock/api\"  // returns {\"ok\": true}\n// after\n\"github_api_url\": \"https://github.com\"  // returns {\"access_token\", \"token_type\", ...}","handlingStrategy":"try-catch","validationCode":"# Ensure no proxy/portal intercepts responses with HTML\nimport httpx\nresp = httpx.get(\"https://github.com\", follow_redirects=True)\nif \"text/html\" in resp.headers.get(\"content-type\", \"\") and \"<html\" in resp.text[:200].lower():\n    print(\"Warning: an intercepting proxy or captive portal is rewriting responses\")","typeGuard":null,"tryCatchPattern":"try:\n    login(server_name)\nexcept RuntimeError as exc:\n    if \"not a valid OAuth token payload\" in str(exc):\n        print(\"Token endpoint returned unexpected JSON. Check proxy, captive portal, or custom base URL:\", exc)\n    raise","preventionTips":["Avoid custom GitHub base-URL overrides unless they are verified OAuth-compatible","Bypass captive portals/SSO gateways for github.com","Keep the package updated for schema changes in the token payload"],"tags":["oauth","schema-validation","pydantic","github"],"backgroundTag":"schema-validation-failed","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}