From 87765d602cc2f41e3f9963af4cbceb547580da21 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 07:50:02 +0800 Subject: [PATCH] fix(vedastro): cut responses are network errors, not 500 (BUG-1504) _post_json turns IncompleteRead, ConnectionResetError, RemoteDisconnected and ssl.SSLError raised while reading into URLError, so snapshot dispatch, range scan and external technique evidence record network_error. The REST health probe also catches http.client.HTTPException. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- scripts/vedastro_rest_bridge.py | 4 +++- scripts/vedastro_service_adapter.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/scripts/vedastro_rest_bridge.py b/scripts/vedastro_rest_bridge.py index e53c1a0f..c4cdf5eb 100644 --- a/scripts/vedastro_rest_bridge.py +++ b/scripts/vedastro_rest_bridge.py @@ -13,6 +13,7 @@ and remains ``official_blocked``. from __future__ import annotations import argparse +import http.client import json import os import sys @@ -155,7 +156,8 @@ def probe_calculate_health(*, opener: Any | None = None, timeout: float = 8) -> "endpoint": API, "transport": "rest", } - except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError) as error: + except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError, http.client.HTTPException) as error: + # HTTPException: IncompleteRead and friends when the body is cut (BUG-1504). return { "status": "official_blocked", "reason": type(error).__name__, diff --git a/scripts/vedastro_service_adapter.py b/scripts/vedastro_service_adapter.py index a947665b..71b055b9 100644 --- a/scripts/vedastro_service_adapter.py +++ b/scripts/vedastro_service_adapter.py @@ -14,6 +14,7 @@ import hashlib import json import os import socket +import ssl import subprocess import sys import threading @@ -2016,6 +2017,12 @@ def _acquire_free_tier_slot(request_url: str) -> dict[str, Any]: } +# A connection cut while reading headers or the body. urllib only wraps errors +# raised while sending, so these reach the caller raw (BUG-1504). RemoteDisconnected +# is both an HTTPException and a ConnectionResetError. +_TRANSPORT_READ_ERRORS = (http.client.HTTPException, ConnectionError, ssl.SSLError) + + def _post_json(endpoint: str, request_preview: dict[str, Any]) -> dict[str, Any] | str: request_url, headers, vedastro_payload = _build_live_request(endpoint, request_preview) req = request.Request( @@ -2024,9 +2031,15 @@ def _post_json(endpoint: str, request_preview: dict[str, Any]) -> dict[str, Any] headers=headers, method="POST", ) - with request.urlopen(req, timeout=_timeout_seconds()) as resp: - raw = resp.read().decode("utf-8") - return json.loads(raw) + try: + with request.urlopen(req, timeout=_timeout_seconds()) as resp: + raw = resp.read().decode("utf-8") + except error.URLError: + raise + except _TRANSPORT_READ_ERRORS as exc: + # Every caller already turns URLError into network_error. + raise error.URLError(f"response_read_failed:{type(exc).__name__}") from exc + return json.loads(raw) def _retry_status_codes() -> set[int]: