Problem
/idd-verify #36 round 3(codex lens)發現一個獨立於 #36 本身修正邏輯的既有缺口:upstream 端的 socket.timeout/TimeoutError 完全不會走到 ThreadingHTTPServer.handle_error(),因此不管 handle_error 怎麼寫都碰不到它——這是標準庫 http.server.BaseHTTPRequestHandler.handle_one_request() 的既有行為,跟 #36 引入的 client/upstream 分類機制無關。
Original text(codex finding,逐字):
「Codex 認為這是本次設計最大的覆蓋缺口(與「標記機制對不對」無關,是標記機制構不到的區域)。四個 upstream I/O 點(_handle 的 e.read() / upstream_resp.read() / urlopen outer except,以及 _forward_streaming 的 upstream_resp.read(1))都只捕捉 (ConnectionResetError, BrokenPipeError)。失敗情境:1. upstream 已回 headers,urlopen() 成功回傳 HTTPResponse;2. upstream 在 body 中途停住,超過 socket timeout;3. read() / read(1) 拋 socket.timeout / TimeoutError;4. 現有 except 不捕捉它 → 例外往上傳;5. stdlib BaseHTTPRequestHandler.handle_one_request() 會捕捉 TimeoutError,印一行 Request timed out: ... 後關連線 return;6. socketserver.process_request_thread() 因此看不到例外,ThreadingHTTPServer.handle_error() 完全不會被呼叫 → 一個真正的 upstream 故障沒有任何 traceback、也沒有任何歸因訊息。」
Type
bug(observability gap,非 #36 的 client/upstream 分類邏輯本身)
Expected
upstream 逾時(socket timeout)發生時,proxy.log 應該留下可歸因的訊息(至少要能分辨「這是 upstream 逾時,不是別的」),而不是完全沉默。
Actual
BaseHTTPRequestHandler.handle_one_request() 在 stdlib 層級就把 TimeoutError 攔截並印一行泛用的 Request timed out: ...(不經過 handle_error(),也不含任何 upstream-specific 的歸因資訊),然後直接關閉連線 return。任何在 handle_error() 裡加的邏輯(含 #36 新增的 client/upstream 分類)對這個路徑完全不可見。
Impact
- upstream 逾時(例如 api.anthropic.com 回應緩慢或掛起)目前只留下一行通用訊息,無法從 log 判斷是哪個 request、什麼情境下逾時
- 若未來要做逾時相關的可觀測性或告警,現有機制在這個路徑上完全構不到
Suggested direction(codex 建議,供 diagnose 階段判斷)
在每個 upstream I/O 邊界(urlopen、upstream_resp.read()、e.read()、upstream_resp.read(1))額外捕捉 TimeoutError,改拋一個不繼承 TimeoutError 的自訂例外(例如 UpstreamTimeoutError(...) from exc),這樣才不會被 handle_one_request() 攔截、能正常傳到 handle_error()。更集中的做法是把四個呼叫點收斂成一個 _upstream_call() wrapper,而非逐點列舉。
Context
源頭:PsychQuant/claude-hot-limit#36 的 verify round 3(pai-ensemble,codex lens,2026-08-26)。#36 本身的 client/upstream 斷線分類已修正並驗證(3 輪 verify),本 issue 是驗證過程中發現的獨立既有缺口,不阻擋 #36 close。
相關檔案:plugins/claude-hot-limit/proxy/rate-limit-proxy.py(_handle() / _forward_streaming() / ThreadingHTTPServer.handle_error())
Problem
/idd-verify #36round 3(codex lens)發現一個獨立於 #36 本身修正邏輯的既有缺口:upstream 端的socket.timeout/TimeoutError完全不會走到ThreadingHTTPServer.handle_error(),因此不管handle_error怎麼寫都碰不到它——這是標準庫http.server.BaseHTTPRequestHandler.handle_one_request()的既有行為,跟 #36 引入的 client/upstream 分類機制無關。Type
bug(observability gap,非 #36 的 client/upstream 分類邏輯本身)
Expected
upstream 逾時(socket timeout)發生時,proxy.log 應該留下可歸因的訊息(至少要能分辨「這是 upstream 逾時,不是別的」),而不是完全沉默。
Actual
BaseHTTPRequestHandler.handle_one_request()在 stdlib 層級就把TimeoutError攔截並印一行泛用的Request timed out: ...(不經過handle_error(),也不含任何 upstream-specific 的歸因資訊),然後直接關閉連線return。任何在handle_error()裡加的邏輯(含 #36 新增的 client/upstream 分類)對這個路徑完全不可見。Impact
Suggested direction(codex 建議,供 diagnose 階段判斷)
在每個 upstream I/O 邊界(
urlopen、upstream_resp.read()、e.read()、upstream_resp.read(1))額外捕捉TimeoutError,改拋一個不繼承TimeoutError的自訂例外(例如UpstreamTimeoutError(...) from exc),這樣才不會被handle_one_request()攔截、能正常傳到handle_error()。更集中的做法是把四個呼叫點收斂成一個_upstream_call()wrapper,而非逐點列舉。Context
源頭:
PsychQuant/claude-hot-limit#36的 verify round 3(pai-ensemble,codex lens,2026-08-26)。#36 本身的 client/upstream 斷線分類已修正並驗證(3 輪 verify),本 issue 是驗證過程中發現的獨立既有缺口,不阻擋 #36 close。相關檔案:
plugins/claude-hot-limit/proxy/rate-limit-proxy.py(_handle()/_forward_streaming()/ThreadingHTTPServer.handle_error())