google-gemini/gemini-cli · error · RuntimeError

Failed to fetch issue #{issue_number} from GitHub API ({resp

Error message

Failed to fetch issue #{issue_number} from GitHub API ({resp.status_code}): {resp.text}

What it means

This RuntimeError is raised by get_issue_details() when the GitHub REST API GET to /repos/{owner}/{repo}/issues/{issue_number} returns any non-200 HTTP status. The error includes the status code and raw response body so the caller can distinguish rate limits (403/429), auth failures (401), not-found (404), and server errors (5xx). It is a hard failure because the eval pipeline cannot proceed without issue data.

Source

Thrown at tools/caretaker-agent/evals/triage/helpers/github_api.py:29


def _get_github_headers() -> Dict[str, str]:
    """
    Optionally retrieves GITHUB_TOKEN (or GH_TOKEN) to authenticate requests.
    """
    token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
    headers = {"Accept": "application/vnd.github.v3+json"}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    return headers


def get_issue_details(owner: str, repo: str, issue_number: int) -> Dict[str, Any]:
    """Queries GitHub REST API for issue details (title, body, createdAt, labels)."""
    url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}"
    resp = requests.get(url, headers=_get_github_headers(), timeout=15)
    if resp.status_code != 200:
        raise RuntimeError(f"Failed to fetch issue #{issue_number} from GitHub API ({resp.status_code}): {resp.text}")
    
    data = resp.json()
    return {
        "owner": owner,
        "repo": repo,
        "number": data.get("number"),
        "title": data.get("title", ""),
        "body": data.get("body", "") or "",
        "createdAt": data.get("created_at", ""),
        "labels": data.get("labels", [])
    }


def get_pr_details(owner: str, repo: str, pr_number: int) -> Dict[str, Any]:
    """Queries GitHub REST API for PR details (title, body, baseRefOid, patch/diff)."""
    url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
    headers = _get_github_headers()
    resp = requests.get(url, headers=headers, timeout=15)

View on GitHub (pinned to 5024443c72)

Solutions

  1. Set a valid GITHUB_TOKEN (or GH_TOKEN) env var with repo read scope and retry.
  2. Verify owner, repo, and issue_number are correct and that the issue exists at the URL in a browser.
  3. If status is 403/429, wait for the rate-limit window to reset (check X-RateLimit-Reset header) or reduce concurrency in the eval runner.
  4. Add a retry with exponential backoff around get_issue_details for transient 5xx responses.
  5. Confirm Cloud Run / network egress to api.github.com is permitted.

Example fix

# before
os.environ.pop('GITHUB_TOKEN', None)  # anonymous, hits rate limit fast
# after
os.environ['GITHUB_TOKEN'] = 'ghp_...'  # authenticated, 5000 req/hr
Defensive patterns

Strategy: retry

Validate before calling

import os, requests

def can_fetch_issue(owner: str, repo: str, issue_number: int) -> bool:
    headers = {'Accept': 'application/vnd.github.v3+json'}
    token = os.environ.get('GITHUB_TOKEN') or os.environ.get('GH_TOKEN')
    if token:
        headers['Authorization'] = f'Bearer {token}'
    resp = requests.get(f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}', headers=headers, timeout=15)
    return resp.status_code == 200

Try / catch

import time
from evals.triage.helpers.github_api import get_issue_details

for attempt in range(3):
    try:
        issue = get_issue_details(owner, repo, issue_number)
        break
    except RuntimeError as e:
        if '403' in str(e) or '429' in str(e):
            time.sleep(2 ** attempt)
        else:
            raise
else:
    raise RuntimeError(f'Could not fetch issue {issue_number} after retries')

Prevention

When it happens

Trigger: Calling get_issue_details(owner, repo, issue_number) with a wrong owner/repo, an issue number that does not exist (404), no token when the repo is private (404/403), an expired/invalid GITHUB_TOKEN (401), or hitting a secondary rate limit (403 with 'rate limit' message, or 429). Network issues typically raise ConnectionError before this guard.

Common situations: GITHUB_TOKEN/GH_TOKEN is unset or expired and the eval tries to fetch a private-repo issue. A typo in owner or repo. The issue was deleted or is actually a PR (use the pulls endpoint). GitHub primary rate limit exhausted (5000 req/hr authenticated). Cloud Run egress blocked or DNS failing producing a non-200 from a proxy.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/eea4422357b54e16. Report an issue: GitHub.