odysseus-dev/odysseus · error · HTTPException
Server has no OAuth config
Error message
Server has no OAuth config
What it means
On GET /oauth/authorize/{server_id}, the server row exists but srv.oauth_config is empty/null, so there is no OAuth configuration to drive the flow and the route returns 400. The field is populated at registration time from the oauth_file/oauth_config form parameters, so this means the server was registered without OAuth credentials.
Source
Thrown at routes/mcp/mcp_routes.py:438
db.commit()
return {"id": server_id, "disabled_count": len(disabled)}
finally:
db.close()
# ── OAuth flow for Google MCP servers ──────────────────────────
@router.get("/oauth/authorize/{server_id}")
def oauth_authorize(server_id: str, request: Request):
"""Show OAuth authorization page with Google sign-in link."""
require_admin(request)
db = SessionLocal()
try:
srv = db.query(McpServer).filter(McpServer.id == server_id).first()
if not srv:
raise HTTPException(404, "Server not found")
if not srv.oauth_config:
raise HTTPException(400, "Server has no OAuth config")
oauth_cfg = _sanitize_mcp_oauth_config(json.loads(srv.oauth_config))
keys_file = oauth_cfg.get("keys_file", "")
if not keys_file or not os.path.exists(keys_file):
raise HTTPException(400, "OAuth keys file not found")
with open(keys_file, encoding="utf-8") as f:
keys_data = json.load(f)
keys = keys_data.get("installed") or keys_data.get("web")
if not keys:
raise HTTPException(400, "Invalid OAuth keys file format")
client_id = keys["client_id"]
scopes = oauth_cfg.get("scopes", [])
# For Desktop App creds, default to localhost — the user will
# paste the resulting URL back if they're on a different device.
redirect_uri = _mcp_oauth_redirect_uri()View on GitHub (pinned to f9235ebbf1)
Solutions
- Re-register or update the server with an oauth_file/oauth_config under the mcp_oauth base dir.
- Confirm registration succeeded with OAuth fields by inspecting the server row (GET /servers) before calling authorize.
- Only call authorize for servers that genuinely need Google auth.
Example fix
# before: registered without oauth
requests.post(f"{base}/servers", data={"transport": "http", "url": u, "name": "gdrive"})
# after
requests.post(f"{base}/servers", data={"transport": "http", "url": u, "name": "gdrive",
"oauth_file": "client_secret.json"}) Defensive patterns
Strategy: validation
Validate before calling
def server_has_oauth(srv_row: dict) -> bool:
return bool(srv_row.get("oauth_config")) Try / catch
On 400 'Server has no OAuth config', re-register the server with oauth_file instead of retrying authorize.
Prevention
- Only show OAuth authorize buttons for servers registered with OAuth credentials.
- Verify registration persisted oauth_config before starting the flow.
When it happens
Trigger: Registering a server with transport sse/http and no oauth_file/oauth_config, then hitting authorize; registering with an oauth_file whose path failed confinement (error 641) so the config never persisted; a stdio server that never carries OAuth.
Common situations: Assuming every listed server supports Google OAuth; OAuth config lost during registration due to path validation; server created before the OAuth feature existed.
Related errors
- Unknown device-flow provider: ${provider}
- OAuth keys file not found
- Invalid OAuth keys file format
- OAuth keys/token file not configured
- HTTP ${r.status}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/9b812a192b3e7eb4.
Report an issue: GitHub.