{"record":{"id":"60a8c9eb11859c8e","repo":"odysseus-dev/odysseus","slug":"oauth-keys-file-not-found","errorCode":null,"errorMessage":"OAuth keys file not found","messagePattern":"OAuth keys file not found","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/mcp/mcp_routes.py","lineNumber":443,"sourceCode":"\n    # ── OAuth flow for Google MCP servers ──────────────────────────\n\n    @router.get(\"/oauth/authorize/{server_id}\")\n    def oauth_authorize(server_id: str, request: Request):\n        \"\"\"Show OAuth authorization page with Google sign-in link.\"\"\"\n        require_admin(request)\n        db = SessionLocal()\n        try:\n            srv = db.query(McpServer).filter(McpServer.id == server_id).first()\n            if not srv:\n                raise HTTPException(404, \"Server not found\")\n            if not srv.oauth_config:\n                raise HTTPException(400, \"Server has no OAuth config\")\n\n            oauth_cfg = _sanitize_mcp_oauth_config(json.loads(srv.oauth_config))\n            keys_file = oauth_cfg.get(\"keys_file\", \"\")\n            if not keys_file or not os.path.exists(keys_file):\n                raise HTTPException(400, \"OAuth keys file not found\")\n\n            with open(keys_file, encoding=\"utf-8\") as f:\n                keys_data = json.load(f)\n            keys = keys_data.get(\"installed\") or keys_data.get(\"web\")\n            if not keys:\n                raise HTTPException(400, \"Invalid OAuth keys file format\")\n\n            client_id = keys[\"client_id\"]\n            scopes = oauth_cfg.get(\"scopes\", [])\n\n            # For Desktop App creds, default to localhost — the user will\n            # paste the resulting URL back if they're on a different device.\n            redirect_uri = _mcp_oauth_redirect_uri()\n\n            params = {\n                \"client_id\": client_id,\n                \"redirect_uri\": redirect_uri,\n                \"response_type\": \"code\",","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/mcp/mcp_routes.py#L425-L461","documentation":"On GET /oauth/authorize/{server_id}, the sanitized oauth_config's keys_file is empty or the file does not exist on disk (os.path.exists check), returning 400. The keys_file is a Google OAuth client-credentials JSON; the sanitize step may also have rewritten a relative path under the mcp_oauth base, so the file must actually live at that resolved location.","triggerScenarios":"oauth_config references keys_file that was deleted, moved, or never copied into the mcp_oauth dir; container deployments where the base dir is an unmounted volume and files vanish on restart; path passed as absolute and rejected/rewritten by sanitization.","commonSituations":"creds file gitignored and missing after a fresh clone/CI deploy; Docker volume not mounted for mcp_oauth; keys file renamed after rotation.","solutions":["Copy the Google client-secret JSON into the mcp_oauth base directory and reference it by relative filename in oauth_config.","Verify the resolved path exists (os.path.exists) before calling authorize.","In containers, mount a persistent volume over the mcp_oauth base dir so credential files survive restarts."],"exampleFix":"# before\noauth_config = {\"keys_file\": \"/home/me/creds/secret.json\"}\n\n# after\nbase = _mcp_oauth_base_dir()\nshutil.copy(\"/home/me/creds/secret.json\", Path(base) / \"secret.json\")\noauth_config = {\"keys_file\": \"secret.json\"}","handlingStrategy":"validation","validationCode":"import os\ndef keys_file_ready(oauth_cfg: dict, base) -> bool:\n    kf = oauth_cfg.get(\"keys_file\", \"\")\n    return bool(kf) and os.path.exists(os.path.join(base, kf))","typeGuard":null,"tryCatchPattern":"On 400 'OAuth keys file not found', copy the credentials into the mcp_oauth base and re-register; do not retry the authorize URL.","preventionTips":["Keep credential files inside the mcp_oauth directory.","Mount persistent storage for mcp_oauth in containers.","Check os.path.exists before starting any OAuth flow."],"tags":["mcp","oauth","filesystem","config"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}