abi/screenshot-to-code · warning · HTTPException

Run has no captured output

Error message

Run has no captured output

What it means

Raised by GET /agent-runs/{run_id}/output (404) when the run exists in the index but its directory contains neither final_selfcontained.html nor final.html. The route serves captured artifacts, so it 404s when generation never finished far enough to write an output file, or the files were deleted from disk even though the DB row remains.

Source

Thrown at backend/routes/agent_runs.py:211

                except json.JSONDecodeError:
                    # A crashed writer can leave a torn final line.
                    continue
                if not include_stream_deltas and event.get("type") == "stream_delta":
                    continue
                events.append(event)
    return AgentRunDetailResponse(run=run, events=events)


@router.get("/agent-runs/{run_id}/output")
async def get_agent_run_output(run_id: str) -> HTMLResponse:
    _fetch_run(run_id)
    run_dir = os.path.join(get_agent_runs_directory(), run_id)
    for candidate in ("final_selfcontained.html", "final.html"):
        path = os.path.join(run_dir, candidate)
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                return HTMLResponse(content=f.read())
    raise HTTPException(status_code=404, detail="Run has no captured output")


@router.get("/agent-runs/{run_id}/assets/{filename}")
async def get_agent_run_asset(run_id: str, filename: str) -> FileResponse:
    _fetch_run(run_id)
    assets_dir = os.path.join(get_agent_runs_directory(), run_id, "assets")
    # basename() strips any traversal components before the containment check.
    safe_name = os.path.basename(filename)
    path = os.path.realpath(os.path.join(assets_dir, safe_name))
    if not path.startswith(os.path.realpath(assets_dir) + os.sep):
        raise HTTPException(status_code=400, detail="Invalid asset path")
    if not os.path.isfile(path):
        raise HTTPException(status_code=404, detail="Asset not found")
    return FileResponse(path)


@router.post("/agent-runs/prune", response_model=PruneAgentRunsResponse)
async def prune_agent_runs(request: PruneAgentRunsRequest) -> PruneAgentRunsResponse:

View on GitHub (pinned to d026163f58)

Solutions

  1. Check the run's status via GET /agent-runs/{run_id} and retry once it is complete.
  2. List the run directory under the runs_directory returned by GET /agent-runs and confirm final.html / final_selfcontained.html exist.
  3. If files were deleted, prune the run (POST /agent-runs/prune) so the stale DB row is removed.
  4. If generation failed, re-run the generation that produces the output.

Example fix

# before
resp = client.get(f"/agent-runs/{run_id}/output")  # 404 'Run has no captured output'

# after
detail = client.get(f"/agent-runs/{run_id}").json()
if detail["run"]["status"] != "completed":
    wait_for_completion(run_id)
resp = client.get(f"/agent-runs/{run_id}/output")
Defensive patterns

Strategy: validation

Validate before calling

detail = client.get(f"/agent-runs/{run_id}").json()
status = detail["run"]["status"]
if status != "completed":
    raise RuntimeError(f"run not finished (status={status}); no output yet")

Type guard

def run_has_output(detail: dict) -> bool:
    """Heuristic: only completed runs capture final HTML output."""
    return detail["run"].get("status") == "completed"

Try / catch

try:
    resp = client.get(f"/agent-runs/{run_id}/output")
    resp.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 404 and "captured output" in e.response.text:
        resp = None  # still running or artifacts missing
    else:
        raise

Prevention

When it happens

Trigger: Calling /agent-runs/{run_id}/output for a run that is still in progress, failed mid-generation, or whose run directory was partially cleaned (DB row kept, HTML files removed).

Common situations: Polling the output URL while the agent is still running; a crashed/killed generation; disk cleanup that removed run directories but not the SQLite index; checking output of a run that only produced assets.

Related errors


AI-assisted analysis of abi/screenshot-to-code@d026163f58 (2026-08-14). Data as JSON: /api/errors/a8f9c3e2f820793c. Report an issue: GitHub.