abi/screenshot-to-code · error · HTTPException

Invalid run id

Error message

Invalid run id

What it means

Thrown by _fetch_run() in the agent-runs router when the run_id path parameter does not match RUN_ID_PATTERN (backend/fs_logging/agent_runs.py:52: ^run_\d{8}_\d{6}_[0-9a-f]{8}$, e.g. run_20260814_153000_ab12cd34). It is a 400 FastAPI HTTPException raised before any database or filesystem access, so the run was never looked up. The strict pattern exists because run_id is joined into filesystem paths, so it doubles as path-traversal protection.

Source

Thrown at backend/routes/agent_runs.py:137

    data = dict(zip(_RUN_COLUMNS, row))
    data["has_unpriced_calls"] = bool(data["has_unpriced_calls"])
    return AgentRunSummary(**data)


def _directory_size_bytes(path: str) -> int:
    total = 0
    for root, _, files in os.walk(path):
        for name in files:
            try:
                total += os.path.getsize(os.path.join(root, name))
            except OSError:
                continue
    return total


def _fetch_run(run_id: str) -> AgentRunSummary:
    if not RUN_ID_PATTERN.match(run_id):
        raise HTTPException(status_code=400, detail="Invalid run id")
    if not os.path.isfile(get_agent_runs_db_path()):
        raise HTTPException(status_code=404, detail="No runs recorded")
    conn = open_index_db()
    try:
        row = conn.execute(
            f"SELECT {', '.join(_RUN_COLUMNS)} FROM runs WHERE run_id = ?",
            (run_id,),
        ).fetchone()
    finally:
        conn.close()
    if row is None:
        raise HTTPException(status_code=404, detail="Run not found")
    return _row_to_summary(row)


@router.get("/agent-runs", response_model=AgentRunListResponse)
async def list_agent_runs(limit: int = 200) -> AgentRunListResponse:
    runs_directory = get_agent_runs_directory()

View on GitHub (pinned to d026163f58)

Solutions

  1. Fetch ids from GET /agent-runs and pass run.run_id verbatim instead of constructing or editing ids by hand.
  2. Verify the id against the regex ^run_\d{8}_\d{6}_[0-9a-f]{8}$ before issuing the request.
  3. Check for whitespace or URL-encoding damage (e.g. %20, unencoded underscores) in the id you are sending.
  4. If you maintain a producer of run ids, ensure it formats them as run_ + date + time + 8 lowercase hex chars.

Example fix

# before
resp = client.get(f"/agent-runs/{run_id}")  # run_id = 'run-2026-08-14' -> 400

# after
import re
RUN_ID_RE = re.compile(r"^run_\d{8}_\d{6}_[0-9a-f]{8}$")
if not RUN_ID_RE.match(run_id):
    raise ValueError(f"malformed run id: {run_id!r}
resp = client.get(f"/agent-runs/{run_id}")
Defensive patterns

Strategy: validation

Validate before calling

import re
RUN_ID_RE = re.compile(r"^run_\d{8}_\d{6}_[0-9a-f]{8}$")

def is_valid_run_id(run_id: str) -> bool:
    return bool(RUN_ID_RE.match(run_id))

# before the call
if not is_valid_run_id(run_id):
    raise ValueError(f"malformed run id: {run_id!r}")

Type guard

def is_valid_run_id(run_id: str) -> bool:
    """True when run_id matches run_YYYYMMDD_HHMMSS_xxxxxxxx (8 lowercase hex)."""
    return bool(re.match(r"^run_\d{8}_\d{6}_[0-9a-f]{8}$", run_id))

Try / catch

try:
    resp = client.get(f"/agent-runs/{run_id}")
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and "Invalid run id" in e.response.text:
        raise ValueError(f"bad run id format: {run_id!r}") from e
    raise

Prevention

When it happens

Trigger: Any GET /agent-runs/{run_id}, /agent-runs/{run_id}/output, or /agent-runs/{run_id}/assets/{filename} call where run_id lacks the run_YYYYMMDD_HHMMMM_8hexchars shape: truncated ids, ids copied with a missing segment, URL-decoded ids with stray slashes, or entirely fabricated ids.

Common situations: Frontend passes a stale or hand-edited run id; a caller reconstructs the id from a timestamp instead of reading it from the list endpoint; the id is copy-pasted with whitespace; a script iterates directory names that do not follow the run_ naming convention.

Related errors


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