mvanhorn/last30days-skill · error · RuntimeError

Gemini request failed: {exc}

Error message

Gemini request failed: {exc}

What it means

The URLError branch of call_gemini_judge(): the request never got an HTTP response — DNS failure, connection refused, TLS error, or the 120-second timeout expiring (socket.timeout is wrapped in URLError). RuntimeError preserves the original exception as __cause__.

Source

Thrown at skills/last30days/scripts/evaluate_search_quality.py:233

def call_gemini_judge(api_key: str, model: str, prompt: str) -> dict[str, Any]:
    body = {
        "contents": [{"parts": [{"text": prompt}]}],
        "generationConfig": {"temperature": 0, "responseMimeType": "application/json"},
    }
    request = Request(
        GEMINI_API_URL.format(model=model, api_key=api_key),
        data=json.dumps(body).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urlopen(request, timeout=120) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        detail = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"Gemini HTTP {exc.code}: {detail}") from exc
    except URLError as exc:
        raise RuntimeError(f"Gemini request failed: {exc}") from exc
    return json.loads(extract_gemini_text(payload))


def build_judge_prompt(topic: str, query_type: str, items: list[dict[str, Any]]) -> str:
    item_lines = []
    for item in items:
        item_lines.append(
            "\n".join([
                f"- id: {item['key']}",
                f"  source: {item['source']}",
                f"  title: {item['text'][:220]}",
                f"  url: {item['url']}",
                f"  date: {item.get('date') or 'unknown'}",
            ])
        )
    return f"""
Judge search-result relevance for a last-30-days research tool.

View on GitHub (pinned to c7460f6114)

Solutions

  1. Check basic connectivity: curl -sS https://generativelanguage.googleapis.com from the same environment.
  2. Unset or fix HTTP_PROXY/HTTPS_PROXY/ALL_PROXY if a stale proxy is configured.
  3. If timeouts (reason contains 'timed out'), reduce the number of items passed to build_judge_prompt or raise the timeout in urlopen.
  4. Retry with backoff for transient DNS/socket errors before giving up.

Example fix

# before
with urlopen(request, timeout=120) as response:

# after
with urlopen(request, timeout=300) as response:
Defensive patterns

Strategy: retry

Validate before calling

import socket
socket.create_connection(('generativelanguage.googleapis.com', 443), timeout=10)  # preflight

Try / catch

try:
    return call_gemini_judge(api_key, model, prompt)
except RuntimeError as exc:
    if 'request failed' not in str(exc):
        raise
    # network-level failure: backoff once, then report connectivity problem
    time.sleep(5)
    return call_gemini_judge(api_key, model, prompt)

Prevention

When it happens

Trigger: urlopen(request, timeout=120) raising URLError: no route to generativelanguage.googleapis.com (offline sandbox/CI), proxy or corporate firewall blocking the host, DNS resolution failure, or generation exceeding the 120s timeout (socket timeout surfaces as URLError reason).

Common situations: CI runners without network egress; local proxy env vars (HTTP_PROXY/HTTPS_PROXY) pointing at a dead proxy; IPv6-only misrouting; very large judge prompts making the model exceed 120s.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/12f403392e5f243e. Report an issue: GitHub.