{"record":{"id":"2c27933b614c1605","repo":"odysseus-dev/odysseus","slug":"could-not-resolve-endpoint-credentials","errorCode":null,"errorMessage":"Could not resolve endpoint credentials","messagePattern":"Could not resolve endpoint credentials","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/webhook/webhook_routes.py","lineNumber":337,"sourceCode":"            finally:\n                db.close()\n\n            if not ep:\n                raise HTTPException(400,\n                    \"No session, api_key, or configured endpoints. \"\n                    \"Pass api_key + model, or configure an endpoint in Admin.\")\n\n            base_url = normalize_base(ep.base_url)\n            endpoint_url = build_chat_url(base_url)\n            model = body.model or \"auto\"\n            api_key = ep.api_key\n            if getattr(ep, \"provider_auth_id\", None):\n                try:\n                    from src.endpoint_resolver import resolve_endpoint_runtime\n                    base_url, api_key = resolve_endpoint_runtime(ep, owner=token_owner)\n                    endpoint_url = build_chat_url(base_url)\n                except Exception:\n                    raise HTTPException(500, \"Could not resolve endpoint credentials\")\n\n            if model == \"auto\":\n                try:\n                    async with httpx.AsyncClient(timeout=5) as client:\n                        models_url = build_models_url(base_url)\n                        hdrs = build_headers(api_key, base_url)\n                        if models_url:\n                            resp = await client.get(models_url, headers=hdrs)\n                            resp.raise_for_status()\n                            data = resp.json()\n                            items = data if isinstance(data, list) else (data.get(\"data\") or [])\n                            ids = [m.get(\"id\") for m in items if isinstance(m, dict) and m.get(\"id\")]\n                            if not ids and isinstance(data, dict):\n                                ids = [\n                                    m.get(\"name\") or m.get(\"model\")\n                                    for m in (data.get(\"models\") or [])\n                                    if m.get(\"name\") or m.get(\"model\")\n                                ]","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/webhook/webhook_routes.py#L319-L355","documentation":"HTTP 500 from POST /v1/chat Case 3: the selected ModelEndpoint has a provider_auth_id, and resolve_endpoint_runtime(ep, owner=token_owner) raised while exchanging stored provider credentials for a live base_url + api_key. The bare except hides the cause (expired/revoked OAuth refresh token, missing provider auth record, upstream IdP failure), surfacing only 'Could not resolve endpoint credentials'.","triggerScenarios":"An endpoint bound to a provider OAuth whose refresh token expired or was revoked; the provider_auth row deleted but the endpoint still references it; token_owner lacking access to the provider auth; provider auth server unreachable.","commonSituations":"Google/Azure-style provider OAuths that expire after weeks; re-authenticating the provider in Admin but the endpoint keeps a stale reference; sandbox environments with no network path to the IdP.","solutions":["Re-authorize the provider in Admin (refresh the provider auth) and retry","Check that the endpoint's provider_auth_id points to an existing, valid provider-auth record","As a workaround, replace the provider-auth-backed endpoint with a plain base_url + api_key endpoint","Inspect server logs around the 500 for the swallowed underlying exception"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if resp.status_code == 500 and 'resolve endpoint credentials' in detail:\n    # fall back to a plain api_key ModelEndpoint, and page admin to re-auth the provider\n    switch_to_static_key_endpoint()","preventionTips":["Track provider-OAuth expiry and re-authenticate before tokens lapse","Keep a static-key endpoint as a fallback for provider-auth-backed endpoints","Log the underlying exception server-side — the route swallows it"],"tags":["credentials","oauth","endpoint","http-500","chat"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}