abi/screenshot-to-code · error · HTTPException

Error capturing screenshot: {str(e)}

Error message

Error capturing screenshot: {str(e)}

What it means

Catch-all HTTPException(500, f"Error capturing screenshot: {e}") raised by /api/screenshot for any failure that is not a ValueError. In practice this wraps the generic "Error taking screenshot" Exception from capture_screenshot (screenshotone API non-200/empty) and httpx transport errors (DNS failure, connection refused, 60s timeout). The detail string echoes the original exception message, so its content tells you which layer failed.

Source

Thrown at backend/routes/screenshot.py:107

    api_key = request.apiKey

    try:
        # Normalize the URL
        normalized_url = normalize_url(url)
        
        # Capture screenshot with normalized URL
        image_bytes = await capture_screenshot(normalized_url, api_key=api_key)

        # Convert the image bytes to a data url
        data_url = bytes_to_data_url(image_bytes, "image/png")

        return ScreenshotResponse(url=data_url)
    except ValueError as e:
        # Handle URL normalization errors
        raise HTTPException(status_code=500, detail=str(e))
    except Exception as e:
        # Handle other errors
        raise HTTPException(status_code=500, detail=f"Error capturing screenshot: {str(e)}")

View on GitHub (pinned to d026163f58)

Solutions

  1. Read the detail substring: 'Error taking screenshot' means upstream rejected the request — validate the apiKey and quota (see error 84)
  2. If the detail names httpx.ConnectError/ConnectTimeout: check outbound network/DNS from the backend host
  3. If httpx.ReadTimeout: retry once, or raise the httpx timeout in capture_screenshot beyond 60s
  4. Reproduce with the same URL from a working environment to isolate target-specific blocking

Example fix

# before
resp = await client.post("/api/screenshot", json=req)
if resp.status_code >= 400:
    raise RuntimeError(resp.text)  # loses cause

# after (structured handling)
try:
    resp = await client.post("/api/screenshot", json=req)
    resp.raise_for_status()
except httpx.HTTPStatusError:
    detail = resp.json().get("detail", "")
    if "Unsupported protocol" in detail:
        raise ValueError(f"Bad URL: {url}")
    raise RuntimeError(f"Screenshot failed: {detail}")
Defensive patterns

Strategy: try-catch

Validate before calling

def preflight(url: str, api_key: str) -> None:
    validate_screenshot_request(url, api_key)  # catches scheme/key issues client-side
    socket.create_connection((urlparse(url).hostname, 443), timeout=5).close()  # egress check

Try / catch

try:
    shot = await take_screenshot(url)
except httpx.HTTPStatusError as e:
    detail = e.response.json().get("detail", "")
    if "Error taking screenshot" in detail:
        handle_upstream_failure(detail)      # key/quota/target issue
    elif "timeout" in detail.lower():
        return await retry_with_backoff(take_screenshot, url)  # transient
    else:
        handle_network_failure(detail)

Prevention

When it happens

Trigger: POST /api/screenshot where the screenshotone call fails upstream (bad key, quota — detail contains 'Error taking screenshot') or the HTTP request itself fails (detail contains httpx.ConnectError/ReadTimeout); also bytes_to_data_url failures, though those are unlikely.

Common situations: No outbound network access from the backend container; screenshotone key invalid; target host unreachable; slow page exceeding the 60-second httpx timeout.

Related errors


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