Panniantong/Agent-Reach · error · RuntimeError

curl is unavailable for the V2EX TLS fallback

Error message

curl is unavailable for the V2EX TLS fallback

What it means

V2EX channel fetches JSON with urllib first; when Python's TLS stack fails with a known TLS-EOF error, it retries with the OS curl binary (see _get_json_with_curl in agent_reach/channels/v2ex.py:80). This RuntimeError is raised when shutil.which('curl') finds no curl executable, so the TLS fallback path cannot run. It means the environment lacks curl or curl is not on PATH.

Source

Thrown at agent_reach/channels/v2ex.py:90

                or "eof occurred in violation of protocol" in text
            ):
                return True
        for nested in (
            getattr(current, "reason", None),
            current.__cause__,
            current.__context__,
        ):
            if isinstance(nested, BaseException):
                pending.append(nested)
    return False


def _get_json_with_curl(url: str) -> Any:
    """Fetch bounded JSON with the OS curl TLS stack."""
    _validate_api_url(url)
    curl = shutil.which("curl")
    if not curl:
        raise RuntimeError("curl is unavailable for the V2EX TLS fallback")

    command = [
        curl,
        "--fail",
        "--silent",
        "--show-error",
        "--proto",
        "=https",
        "--connect-timeout",
        "5",
        "--max-time",
        str(_TIMEOUT),
        "--max-filesize",
        str(_MAX_RESPONSE_BYTES),
        "--header",
        f"User-Agent: {_UA}",
        "--url",
        url,

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Install curl on the host (apt-get install curl / apk add curl / brew install curl)
  2. Verify `which curl` resolves in the same environment agent-reach runs in (print os.environ['PATH'])
  3. If curl exists but is not found, add its directory to PATH or invoke agent-reach from a shell where PATH includes /usr/bin
  4. As a library workaround, call V2EX URLs directly with your own HTTP client when this error surfaces

Example fix

# before: slim Dockerfile with no curl
FROM python:3.12-slim
RUN pip install agent-reach

# after: install curl so the V2EX TLS fallback works
FROM python:3.12-slim
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN pip install agent-reach
Defensive patterns

Strategy: validation

Validate before calling

import shutil

def v2ex_fallback_available() -> bool:
    """True when the curl TLS fallback can run on this host."""
    return shutil.which("curl") is not None

# before calling V2EXChannel
if not v2ex_fallback_available():
    raise SystemExit("install curl, or route V2EX reads through your own HTTP client")

Prevention

When it happens

Trigger: Any V2EXChannel.read()/search() call where urllib first raises an unexpected-TLS-EOF error and _get_json escalates to _get_json_with_curl, on a host where `curl` is not installed or PATH is stripped (minimal containers, alpine without curl, systemd services with limited PATH).

Common situations: Running agent-reach inside slim Docker images (python:3.x-slim has no curl), restricted CI runners, NixOS/Homebrew environments where curl is not linked into the shell PATH, or subprocess environments where PATH is sanitized.

Understand the failure class

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/7cd7c77fd65fb668. Report an issue: GitHub.