{"record":{"id":"06f0ef1b81a00ad7","repo":"Panniantong/Agent-Reach","slug":"curl-could-not-complete-the-v2ex-tls-fallback","errorCode":null,"errorMessage":"curl could not complete the V2EX TLS fallback","messagePattern":"curl could not complete the V2EX TLS fallback","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent_reach/channels/v2ex.py","lineNumber":120,"sourceCode":"        str(_TIMEOUT),\n        \"--max-filesize\",\n        str(_MAX_RESPONSE_BYTES),\n        \"--header\",\n        f\"User-Agent: {_UA}\",\n        \"--url\",\n        url,\n    ]\n    try:\n        result = subprocess.run(\n            command,\n            capture_output=True,\n            encoding=\"utf-8\",\n            errors=\"replace\",\n            timeout=_TIMEOUT + 2,\n            env=utf8_subprocess_env(),\n        )\n    except (OSError, subprocess.TimeoutExpired) as exc:\n        raise RuntimeError(\"curl could not complete the V2EX TLS fallback\") from exc\n    if result.returncode != 0:\n        raise RuntimeError(\"curl could not complete the V2EX TLS fallback\")\n    if len(result.stdout.encode(\"utf-8\")) > _MAX_RESPONSE_BYTES:\n        raise ValueError(\"V2EX API response exceeds the 1 MiB safety limit\")\n    return json.loads(result.stdout)\n\n\ndef _get_json(url: str) -> Any:\n    \"\"\"Fetch JSON, retrying only Python's known TLS EOF via native curl.\"\"\"\n    try:\n        return _get_json_with_urllib(url)\n    except Exception as exc:\n        if isinstance(exc, ssl.SSLCertVerificationError):\n            raise\n        if not _is_unexpected_tls_eof(exc):\n            raise\n        return _get_json_with_curl(url)\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/channels/v2ex.py#L102-L138","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run `curl -v --max-time 10 https://www.v2ex.com/api/topics/hot.json` manually to see if curl completes at all","If curl hangs, fix the network path (proxy, DNS, firewall) to www.v2ex.com:443","If OSError is exec-related, check `ls -l $(which curl)` for execute permission and that the binary matches the architecture","Retry with backoff in the caller — this error is often transient (timeout-class)"],"exampleFix":"# caller-side retry for the V2EX channel\nimport time\nfrom agent_reach.channels.v2ex import V2EXChannel\n\nch = V2EXChannel()\nfor attempt in range(3):\n    try:\n        data = ch.read(\"https://www.v2ex.com/api/topics/hot.json\")\n        break\n    except RuntimeError:\n        if attempt == 2:\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from agent_reach.channels.v2ex import V2EXChannel\n\nlast = None\nfor attempt in range(3):\n    try:\n        result = V2EXChannel().read(url)\n        break\n    except RuntimeError as exc:\n        # both curl-launch OSError and TimeoutExpired surface here\n        last = exc\nif last is not None and 'result' not in dir():\n    raise last","preventionTips":["Keep V2EX reads under a per-run budget so one hung curl (12s subprocess timeout) cannot stall the agent","Verify outbound connectivity to www.v2ex.com:443 in your deployment health checks","Retry with exponential backoff; timeout-class failures are frequently transient"],"tags":["v2ex","network","curl","subprocess","timeout"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}