abi/screenshot-to-code · warning · ValueError

Unsupported protocol: {parsed.scheme}

Error message

Unsupported protocol: {parsed.scheme}

What it means

ValueError("Unsupported protocol: {scheme}") raised by normalize_url() when urlparse assigns the URL a scheme that is not http/https and the fallback heuristics do not rescue it. The function auto-prefixes https:// for scheme-less inputs and for bare host:port strings, but explicitly refuses ftp://, file://, and anything else scheme-like (mailto:, javascript:, etc.). It is an input-rejection error, not an upstream failure.

Source

Thrown at backend/routes/screenshot.py:35

    # Parse the URL
    parsed = urlparse(url)
    
    # Check if we have a scheme
    if not parsed.scheme:
        # No scheme, add https://
        url = f"https://{url}"
    elif parsed.scheme in ['http', 'https']:
        # Valid scheme, keep as is
        pass
    else:
        # Check if this might be a domain with port (like example.com:8080)
        # urlparse treats this as scheme:netloc, but we want to handle it as domain:port
        if ':' in url and not url.startswith(('http://', 'https://', 'ftp://', 'file://')):
            # Likely a domain:port without protocol
            url = f"https://{url}"
        else:
            # Invalid protocol
            raise ValueError(f"Unsupported protocol: {parsed.scheme}")
    
    return url


def bytes_to_data_url(image_bytes: bytes, mime_type: str) -> str:
    base64_image = base64.b64encode(image_bytes).decode("utf-8")
    return f"data:{mime_type};base64,{base64_image}"


async def capture_screenshot(
    target_url: str, api_key: str, device: str = "desktop"
) -> bytes:
    api_base_url = "https://api.screenshotone.com/take"

    params = {
        "access_key": api_key,
        "url": target_url,
        "full_page": "true",

View on GitHub (pinned to d026163f58)

Solutions

  1. Pass an http:// or https:// URL, or a bare domain like example.com (the function adds https:// itself)
  2. If the target truly is ftp/file content, screenshotting is not supported — fetch the resource by another means
  3. Validate the scheme client-side before calling the endpoint

Example fix

# before
url = "ftp://example.com"
normalized = normalize_url(url)  # ValueError

# after
url = "https://example.com"
normalized = normalize_url(url)  # ok
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def is_screenshotable_url(url: str) -> bool:
    p = urlparse(url)
    if p.scheme in ("http", "https"):
        return True
    return p.scheme == "" and bool(p.netloc or p.path)  # bare domain gets https:// prefixed

Try / catch

try:
    normalize_url(url)
except ValueError as e:
    if str(e).startswith("Unsupported protocol"):
        reject_input_to_user(url)  # input problem, not a server fault

Prevention

When it happens

Trigger: POST /api/screenshot (or any caller of normalize_url) with url="ftp://example.com", url="file:///etc/passwd", or url="mailto:someone@example.com". Note "example.com:8080" does NOT trigger it — the host:port branch converts it to https://example.com:8080.

Common situations: Users paste an ftp:// or file:// link into the screenshot box; automated pipelines pass unvalidated URLs harvested from documents.

Related errors


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