From a877c3f6d9aefdd7338f2189b83d5bc290e17929 Mon Sep 17 00:00:00 2001 From: Es1la Date: Tue, 5 May 2026 08:47:46 -0700 Subject: [PATCH] fix(feishu): tolerate malformed dedup timestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Salvages @Es1la's PR #13632 — a non-numeric timestamp in the persisted feishu dedup state crashed adapter startup with ValueError/TypeError from the unguarded float() call. Wrap the float() conversion in try/except; skip the bad key and keep loading the rest. The original PR also restructured existing TestDedupTTL tests to use tempfile.TemporaryDirectory + HERMES_HOME patching — that was test-hygiene scope creep unrelated to the bug. Kept only the malformed-timestamp fix and added a focused regression test. --- gateway/platforms/feishu.py | 9 ++++++++- tests/gateway/test_feishu.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/gateway/platforms/feishu.py b/gateway/platforms/feishu.py index 40711d9680..e1528b9bca 100644 --- a/gateway/platforms/feishu.py +++ b/gateway/platforms/feishu.py @@ -3953,7 +3953,14 @@ class FeishuAdapter(BasePlatformAdapter): if isinstance(seen_data, list): entries: Dict[str, float] = {str(item).strip(): 0.0 for item in seen_data if str(item).strip()} elif isinstance(seen_data, dict): - entries = {k: float(v) for k, v in seen_data.items() if isinstance(k, str) and k.strip()} + entries = {} + for key, value in seen_data.items(): + if not isinstance(key, str) or not key.strip(): + continue + try: + entries[key] = float(value) + except (TypeError, ValueError): + continue else: return # Filter out TTL-expired entries (entries saved with ts=0.0 are treated as immortal diff --git a/tests/gateway/test_feishu.py b/tests/gateway/test_feishu.py index 0444261b18..f4ac80f2e1 100644 --- a/tests/gateway/test_feishu.py +++ b/tests/gateway/test_feishu.py @@ -3197,6 +3197,37 @@ class TestDedupTTL(unittest.TestCase): with patch.object(adapter, "_persist_seen_message_ids"): self.assertFalse(adapter._is_duplicate("om_old")) + @patch.dict(os.environ, {}, clear=True) + def test_load_tolerates_malformed_timestamp_values(self): + """Regression #13632 — a non-numeric timestamp in the persisted + dedup state must not crash adapter startup. The bad key is + skipped; the rest of the state loads. + """ + import tempfile + from gateway.config import PlatformConfig + from gateway.platforms.feishu import FeishuAdapter + + with tempfile.TemporaryDirectory() as temp_home: + with patch.dict(os.environ, {"HERMES_HOME": temp_home}, clear=True): + adapter = FeishuAdapter(PlatformConfig()) + adapter._dedup_state_path.parent.mkdir(parents=True, exist_ok=True) + adapter._dedup_state_path.write_text( + json.dumps( + { + "message_ids": { + "om_good": time.time(), + "om_bad_str": "not-a-timestamp", + "om_bad_null": None, + } + } + ), + encoding="utf-8", + ) + adapter._load_seen_message_ids() + assert "om_good" in adapter._seen_message_ids + assert "om_bad_str" not in adapter._seen_message_ids + assert "om_bad_null" not in adapter._seen_message_ids + @patch.dict(os.environ, {}, clear=True) def test_persist_saves_timestamps_as_dict(self): from gateway.config import PlatformConfig