From 308757c9997e1d331eb998261acdc76e957ccf8b Mon Sep 17 00:00:00 2001 From: Yijia-Xiao Date: Sun, 14 Jun 2026 07:23:19 +0000 Subject: [PATCH] fix(data): catch http.client transport errors in StockTwits A truncated/incomplete chunked response raises http.client exceptions (IncompleteRead/BadStatusLine) that are not OSErrors, so they bypassed the existing handler and crashed the analysis. Broaden the catch so the fetch degrades to its placeholder string like every other transport failure. --- tests/test_stocktwits_resilience.py | 42 +++++++++++++++++++++++++++ tradingagents/dataflows/stocktwits.py | 8 ++--- 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/test_stocktwits_resilience.py diff --git a/tests/test_stocktwits_resilience.py b/tests/test_stocktwits_resilience.py new file mode 100644 index 000000000..145e2267d --- /dev/null +++ b/tests/test_stocktwits_resilience.py @@ -0,0 +1,42 @@ +"""StockTwits fetch degrades (never raises) on transport errors, including the +http.client chunked-transfer exceptions that are not OSErrors (#1024).""" + +from __future__ import annotations + +import http.client +from unittest.mock import patch +from urllib.error import HTTPError + +import pytest + +from tradingagents.dataflows import stocktwits + + +def _raise(exc): + class _Resp: + def __enter__(self_inner): + return self_inner + + def __exit__(self_inner, *a): + return False + + def read(self_inner): + raise exc + return _Resp() + + +@pytest.mark.unit +class StockTwitsResilienceTests: + @pytest.mark.parametrize( + "exc", + [ + http.client.IncompleteRead(b""), + HTTPError("url", 503, "down", {}, None), + TimeoutError("slow"), + ], + ) + def test_transport_errors_return_placeholder(self, exc): + with patch.object(stocktwits, "urlopen", return_value=_raise(exc)): + out = stocktwits.fetch_stocktwits_messages("NVDA") + assert "unavailable" in out.lower() + assert out.startswith(""