Panniantong/Agent-Reach · error · RuntimeError

curl could not complete the V2EX TLS fallback

Error message

curl could not complete the V2EX TLS fallback

What it means

Raised by _get_json_with_curl (agent_reach/channels/v2ex.py:120) when launching curl itself fails: subprocess.run raised OSError (exec format error, permission denied) or curl exceeded its hard timeout (_TIMEOUT + 2 = 12 seconds) and was killed via TimeoutExpired. The original exception is chained with `from exc`.

Source

Thrown at agent_reach/channels/v2ex.py:120

        str(_TIMEOUT),
        "--max-filesize",
        str(_MAX_RESPONSE_BYTES),
        "--header",
        f"User-Agent: {_UA}",
        "--url",
        url,
    ]
    try:
        result = subprocess.run(
            command,
            capture_output=True,
            encoding="utf-8",
            errors="replace",
            timeout=_TIMEOUT + 2,
            env=utf8_subprocess_env(),
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        raise RuntimeError("curl could not complete the V2EX TLS fallback") from exc
    if result.returncode != 0:
        raise RuntimeError("curl could not complete the V2EX TLS fallback")
    if len(result.stdout.encode("utf-8")) > _MAX_RESPONSE_BYTES:
        raise ValueError("V2EX API response exceeds the 1 MiB safety limit")
    return json.loads(result.stdout)


def _get_json(url: str) -> Any:
    """Fetch JSON, retrying only Python's known TLS EOF via native curl."""
    try:
        return _get_json_with_urllib(url)
    except Exception as exc:
        if isinstance(exc, ssl.SSLCertVerificationError):
            raise
        if not _is_unexpected_tls_eof(exc):
            raise
        return _get_json_with_curl(url)

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Run `curl -v --max-time 10 https://www.v2ex.com/api/topics/hot.json` manually to see if curl completes at all
  2. If curl hangs, fix the network path (proxy, DNS, firewall) to www.v2ex.com:443
  3. If OSError is exec-related, check `ls -l $(which curl)` for execute permission and that the binary matches the architecture
  4. Retry with backoff in the caller — this error is often transient (timeout-class)

Example fix

# caller-side retry for the V2EX channel
import time
from agent_reach.channels.v2ex import V2EXChannel

ch = V2EXChannel()
for attempt in range(3):
    try:
        data = ch.read("https://www.v2ex.com/api/topics/hot.json")
        break
    except RuntimeError:
        if attempt == 2:
            raise
        time.sleep(2 ** attempt)
Defensive patterns

Strategy: retry

Try / catch

from agent_reach.channels.v2ex import V2EXChannel

last = None
for attempt in range(3):
    try:
        result = V2EXChannel().read(url)
        break
    except RuntimeError as exc:
        # both curl-launch OSError and TimeoutExpired surface here
        last = exc
if last is not None and 'result' not in dir():
    raise last

Prevention

When it happens

Trigger: V2EX read/search where urllib hit TLS EOF, curl exists, but: the curl binary is not executable, the OS cannot fork (resource limits), or curl hangs past 12s (blackholed network, DNS stall) and subprocess.run raises TimeoutExpired.

Common situations: Containers with PID limits or no exec permission on the curl binary; firewalled networks where TCP connections to v2ex.com:443 silently drop so curl never completes; heavily loaded hosts where fork() fails.

Understand the failure class

Related errors


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