can1357/oh-my-pi · error · HTTPException

{exc.message}

Error message

{exc.message}

What it means

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.

Source

Thrown at python/robomp/src/proxy/server.py:476

                extra={"reason": result.reason, "path": request.url.path},
            )
            raise HTTPException(status.HTTP_401_UNAUTHORIZED, "unauthenticated")
        return body

    # ---- meta ----
    @app.get("/healthz")
    async def healthz() -> dict[str, str]:
        return {"status": "ok"}

    # ---- reads ----
    @app.get("/gh/v1/authenticated_login")
    async def authenticated_login(request: Request) -> dict[str, str]:
        await _authenticate(request)
        github: GitHubClient = request.app.state.github
        try:
            login = await github.get_authenticated_login()
        except GitHubError as exc:
            raise HTTPException(exc.status, exc.message) from exc
        return {"login": login}

    @app.get("/gh/v1/repo")
    async def get_repo(request: Request, repo: str) -> JSONResponse:
        await _authenticate(request)
        github: GitHubClient = request.app.state.github
        try:
            info = await github.get_repo(repo)
        except GitHubError as exc:
            return _gh_error_response(exc)
        return JSONResponse(_serialize(info))

    @app.get("/gh/v1/workflow_runs")
    async def list_workflow_runs(request: Request, repo: str, head_sha: str) -> JSONResponse:
        await _authenticate(request)
        _validate_repo_name(repo)
        github: GitHubClient = request.app.state.github
        try:

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the forwarded message/status in the response to identify the upstream cause
  2. Re-authenticate or regenerate the token with the required scopes (repo / read:org as needed)
  3. Wait for the rate-limit reset or use a token with a higher limit if the status is 403 rate-limited
  4. Retry on 5xx after a short backoff; check the GitHub status page for incidents

Example fix

// before
const { login } = await res.json(); // throws on 4xx/5xx
// after
const res = await fetch(proxy + "/gh/v1/login", { headers });
if (!res.ok) {
  const detail = await res.text();
  if (res.status === 403) await backoffForRateLimit(res);
  throw new Error(`login failed (${res.status}): ${detail}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap upstream probe before relying on the token
const probe = await fetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${token}` } });
if (probe.status === 403 && probe.headers.get("x-ratelimit-remaining") === "0") {
  throw new Error(`rate limited until ${new Date(+probe.headers.get("x-ratelimit-reset") * 1000)}`);
}

Try / catch

const res = await fetch(proxy + "/gh/v1/login", { headers });
if (!res.ok) {
  const message = await res.text();
  if (res.status === 401 || res.status === 403) throw new Error(`upstream GitHub rejected token (${res.status}): ${message}`);
  if (res.status >= 500) throw new Error(`GitHub server error, retry later: ${message}`);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/3763b846bc5f54cf. Report an issue: GitHub.