Skip to content

review_runner: surface real crash cause instead of "(see pod log)"#55

Merged
tarekziade merged 1 commit into
mainfrom
fix/review-runner-error-detail
Jul 7, 2026
Merged

review_runner: surface real crash cause instead of "(see pod log)"#55
tarekziade merged 1 commit into
mainfrom
fix/review-runner-error-detail

Conversation

@tarekziade

Copy link
Copy Markdown
Collaborator

Problem

A UI review of huggingface/peft#3395 on zai-org/GLM-5.2 failed twice with only:

LLMResponseError: review crashed (see pod log)

…and the pod log was unrecoverable. Two compounding issues:

  1. review_runner.py discarded the detail. Its catch-all reported the generic "{type}: review crashed (see pod log)", throwing away str(exc) — which for LLMResponseError already carries the LLM endpoint's status + body excerpt. (task_runner.py, the write-capable path, already handled this correctly; the read-only review path had regressed.)
  2. (see pod log) is a dead reference. On the self-reported-error path the Job watcher sees status=error from the callback and reaps the runner pod immediately — it never waits for ttlSecondsAfterFinished, and skips the collect_task_result log-tail capture the crash path uses. The pod (and its traceback) is gone within seconds.

Net effect: an upstream HF-router failure for GLM-5.2 left the operator with no legible cause.

Fix

Bring review_runner.run() to parity with task_runner:

  • except LLMResponseErrorformat_llm_error(exc) (status + body excerpt: 429/400/auth become legible).
  • except requests.HTTPErrorformat_github_http_error(exc).
  • catch-all now embeds crash_detail(exc) (cause + crashing frame) instead of (see pod log).
  • crash_detail moves to errors.py (its shared home); task_runner imports it — one copy for both entrypoints.

Also

make format/test built a Python 3.9 venv because bare python3 is 3.9 on stock macOS, violating requires-python >=3.10 (and stalling dependency resolution). The Makefile now selects the newest available ≥3.10 interpreter and hard-fails with a clear message otherwise. CI (3.12) unaffected.

Tests

tests/test_review_runner.py: strengthened the crash test (asserts the cause travels, never "see pod log") and added one proving a 429's status + body land on the job. Full suite: 398 passed, ruff clean.

🤖 Generated with Claude Code

The read-only review runner's catch-all reported a generic
"{type}: review crashed (see pod log)", discarding str(exc). But on the
self-reported-error path serge's watcher reaps the runner pod as soon as
the callback marks the job errored — it never waits for the TTL and skips
the crash-path log-tail capture — so "(see pod log)" is a dead reference:
the pod (and its traceback) is gone within seconds. A GLM-5.2 review that
hit an LLMResponseError from the HF router was left with no legible cause.

Bring review_runner to parity with task_runner: add dedicated
LLMResponseError (status + body excerpt via format_llm_error) and
requests.HTTPError (format_github_http_error) handlers, and embed
crash_detail(exc) in the catch-all. crash_detail moves to errors.py (its
shared home) so both runner entrypoints use one copy.

Also fix the Makefile to select the newest available Python >= 3.10 and
hard-fail otherwise: bare `python3` is 3.9 on stock macOS, which silently
built a venv that violates requires-python.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tarekziade tarekziade merged commit b85410a into main Jul 7, 2026
3 checks passed
@tarekziade tarekziade deleted the fix/review-runner-error-detail branch July 7, 2026 14:19
tarekziade added a commit that referenced this pull request Jul 7, 2026
…ce (#56)

## Why

A GLM-5.2 review of `huggingface/peft#3395` failed with (once PR #55
made it legible):

```
LLM endpoint returned 403 Forbidden: {"error":"This authentication method does not
have sufficient permissions to call Inference Providers on behalf of org huggingface"}
```

The HF token can list models fine but lacks permission to **call
Inference Providers on behalf of the org**. Nothing on the Settings page
let an admin catch that ahead of time — you only found out when a review
died.

## What

New endpoint **`POST /admin/providers/{id}/test`** that exercises a
saved config end-to-end:

- Resolves endpoint / model / billing **exactly as a real review would**
(`_api_base_for_provider`, `default_model` → provider default,
`X-HF-Bill-To` for HF).
- Makes **one tiny `max_tokens=1` chat-completion** with the stored key.
A `/models` GET would *not* catch this failure class — the missing
permission only shows on a real inference call, and `max_tokens=1` keeps
it cheap (the 401/403 fires before any generation).
- Returns `{ok: true, model}` on success, or `{ok: false, error}`
carrying the provider's own message. Always **HTTP 200** — a bad token
is a verdict, not a server error.

Settings page gets a per-row **Test** button with inline ✓/✗ feedback;
failures also surface the provider's error text in the banner, so the
fix (grant the token the *Inference Providers* permission / authorize it
for the org) is obvious.

## Tests

`tests/test_webapp_admin.py::ProviderTestEndpointTests` — valid token →
`ok` + model + billing org echoed; a 403 → `ok:false` with the provider
message intact; missing config → 404. Full suite **401 passed**, ruff
clean.

## Note

Depends on the error-surfacing fix from #55 (already merged) for the
runner path, but this endpoint is independent and works on its own.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant