{"record":{"id":"3763b846bc5f54cf","repo":"can1357/oh-my-pi","slug":"exc-message","errorCode":null,"errorMessage":"{exc.message}","messagePattern":"\\{exc\\.message\\}","errorType":"http","errorClass":"HTTPException","httpStatus":null,"severity":"error","filePath":"python/robomp/src/proxy/server.py","lineNumber":476,"sourceCode":"                extra={\"reason\": result.reason, \"path\": request.url.path},\n            )\n            raise HTTPException(status.HTTP_401_UNAUTHORIZED, \"unauthenticated\")\n        return body\n\n    # ---- meta ----\n    @app.get(\"/healthz\")\n    async def healthz() -> dict[str, str]:\n        return {\"status\": \"ok\"}\n\n    # ---- reads ----\n    @app.get(\"/gh/v1/authenticated_login\")\n    async def authenticated_login(request: Request) -> dict[str, str]:\n        await _authenticate(request)\n        github: GitHubClient = request.app.state.github\n        try:\n            login = await github.get_authenticated_login()\n        except GitHubError as exc:\n            raise HTTPException(exc.status, exc.message) from exc\n        return {\"login\": login}\n\n    @app.get(\"/gh/v1/repo\")\n    async def get_repo(request: Request, repo: str) -> JSONResponse:\n        await _authenticate(request)\n        github: GitHubClient = request.app.state.github\n        try:\n            info = await github.get_repo(repo)\n        except GitHubError as exc:\n            return _gh_error_response(exc)\n        return JSONResponse(_serialize(info))\n\n    @app.get(\"/gh/v1/workflow_runs\")\n    async def list_workflow_runs(request: Request, repo: str, head_sha: str) -> JSONResponse:\n        await _authenticate(request)\n        _validate_repo_name(repo)\n        github: GitHubClient = request.app.state.github\n        try:","sourceCodeStart":458,"sourceCodeEnd":494,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy/server.py#L458-L494","documentation":"After a successful authenticated_login call to the proxy, the proxy asks the upstream GitHub API for the authenticated user's login. If the upstream GitHubClient raises GitHubError, the proxy forwards its status and message as an HTTPException — so this error surfaces the real GitHub API failure (401 bad credentials, 403 rate limit, 5xx, etc.) through the proxy.","triggerScenarios":"GET /gh/v1/login with a token that passes local auth but fails upstream: revoked mid-flight, lacking required scopes, rate-limited (403 with X-RateLimit-Remaining: 0), or GitHub returning a server error.","commonSituations":"Fine-grained PAT without account-level read permission; org SSO not authorized for the token; exhausted API rate limit on shared runners; transient GitHub 5xx incidents.","solutions":["Read the forwarded message/status in the response to identify the upstream cause","Re-authenticate or regenerate the token with the required scopes (repo / read:org as needed)","Wait for the rate-limit reset or use a token with a higher limit if the status is 403 rate-limited","Retry on 5xx after a short backoff; check the GitHub status page for incidents"],"exampleFix":"// before\nconst { login } = await res.json(); // throws on 4xx/5xx\n// after\nconst res = await fetch(proxy + \"/gh/v1/login\", { headers });\nif (!res.ok) {\n  const detail = await res.text();\n  if (res.status === 403) await backoffForRateLimit(res);\n  throw new Error(`login failed (${res.status}): ${detail}`);\n}","handlingStrategy":"try-catch","validationCode":"// cheap upstream probe before relying on the token\nconst probe = await fetch(\"https://api.github.com/user\", { headers: { Authorization: `Bearer ${token}` } });\nif (probe.status === 403 && probe.headers.get(\"x-ratelimit-remaining\") === \"0\") {\n  throw new Error(`rate limited until ${new Date(+probe.headers.get(\"x-ratelimit-reset\") * 1000)}`);\n}","typeGuard":null,"tryCatchPattern":"const res = await fetch(proxy + \"/gh/v1/login\", { headers });\nif (!res.ok) {\n  const message = await res.text();\n  if (res.status === 401 || res.status === 403) throw new Error(`upstream GitHub rejected token (${res.status}): ${message}`);\n  if (res.status >= 500) throw new Error(`GitHub server error, retry later: ${message}`);\n}","preventionTips":["Grant fine-grained PATs the scopes the proxy endpoints need","Authorize tokens for org SSO","Monitor rate limits and rotate tokens before expiry","Retry 5xx with backoff; surface 4xx messages to the user"],"tags":["github-api","http-401","rate-limit","error-propagation"],"backgroundTag":"github-api-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}