{"repo":"NousResearch/hermes-agent","public_id":"27370","display_label":"NameError for _pool_may_recover_from_rate_limit in conversation_loop","summary":"Nearly all members concern a NameError caused by a missing _ra() prefix or import for _pool_may_recover_from_rate_limit in conversation_loop.py, which breaks rate-limit fallback behavior. One member proposes replacing the _ra() lazy-import pattern with explicit dependency injection.","subsystem":"agent","counts":{"members":18,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":9,"merged_prs":1,"attempt_prs":8},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":28345},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"verified","last_verified_at":"2026-05-19T03:04:57+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.16156862745098033},"fix_edges":[{"issue_number":27370,"pr_number":28345,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":27402,"pr_number":28345,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":27465,"pr_number":28345,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":27719,"pr_number":28345,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":27370,"title":"[Bug] NameError: '_pool_may_recover_from_rate_limit' is not defined in conversation_loop.py","url":null,"state":"closed"},{"kind":"issue","number":27402,"title":"NameError: '_pool_may_recover_from_rate_limit' is not defined in conversation_loop.py","url":null,"state":"closed"},{"kind":"issue","number":27465,"title":"bug: NameError '_pool_may_recover_from_rate_limit' in conversation_loop.py on rate limit (v0.14.0)","url":null,"state":"closed"},{"kind":"issue","number":27719,"title":"[Bug]: Fallback fails with NameError when primary provider (DeepSeek) runs out of tokens","url":null,"state":"closed"},{"kind":"issue","number":28389,"title":"NameError: _pool_may_recover_from_rate_limit not imported into extracted agent/conversation_loop.py","url":null,"state":"closed"},{"kind":"issue","number":28773,"title":"`NameError: _pool_may_recover_from_rate_limit` in `agent/conversation_loop.py:2335` — fallback chain never activates on HTTP 429","url":null,"state":"closed"},{"kind":"issue","number":29202,"title":"NameError: _pool_may_recover_from_rate_limit is not defined — missing _ra() prefix in conversation_loop.py","url":null,"state":"closed"},{"kind":"issue","number":29621,"title":"[Bug]: After main model 429, switch to fallback model and report an error.","url":null,"state":"closed"},{"kind":"issue","number":30350,"title":"NameError: _pool_may_recover_from_rate_limit not defined in conversation_loop.py","url":null,"state":"closed"},{"kind":"pr","number":27359,"title":"fix(agent): resolve NameError in rate-limit fallback decision","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#27374","number":27374},{"kind":"pr","value":"#27374","number":27374},{"kind":"pr","value":"#27374","number":27374},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27374","number":27374}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":27374,"title":"fix(agent): wrap _pool_may_recover_from_rate_limit with _ra() lazy import","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#28345","number":28345},{"kind":"pr","value":"#28159","number":28159},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27465","number":27465},{"kind":"pr","value":"#27468","number":27468},{"kind":"pr","value":"#27583","number":27583},{"kind":"pr","value":"#27686","number":27686},{"kind":"pr","value":"#27732","number":27732},{"kind":"pr","value":"#27734","number":27734},{"kind":"pr","value":"#27735","number":27735},{"kind":"pr","value":"#27750","number":27750},{"kind":"pr","value":"#27891","number":27891},{"kind":"pr","value":"#27903","number":27903},{"kind":"pr","value":"#28304","number":28304}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":27468,"title":"fix(conversation_loop): NameError on rate limit — route _pool_may_recover_from_rate_limit through _ra() (#27465)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#28345","number":28345},{"kind":"pr","value":"#28159","number":28159},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27465","number":27465},{"kind":"pr","value":"#27468","number":27468},{"kind":"pr","value":"#27583","number":27583},{"kind":"pr","value":"#27686","number":27686},{"kind":"pr","value":"#27732","number":27732},{"kind":"pr","value":"#27734","number":27734},{"kind":"pr","value":"#27735","number":27735},{"kind":"pr","value":"#27750","number":27750},{"kind":"pr","value":"#27891","number":27891},{"kind":"pr","value":"#27903","number":27903},{"kind":"pr","value":"#28304","number":28304}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":27583,"title":"fix: prefix _pool_may_recover_from_rate_limit with module name","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#28345","number":28345},{"kind":"pr","value":"#28159","number":28159},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27465","number":27465},{"kind":"pr","value":"#27468","number":27468},{"kind":"pr","value":"#27583","number":27583},{"kind":"pr","value":"#27686","number":27686},{"kind":"pr","value":"#27732","number":27732},{"kind":"pr","value":"#27734","number":27734},{"kind":"pr","value":"#27735","number":27735},{"kind":"pr","value":"#27750","number":27750},{"kind":"pr","value":"#27891","number":27891},{"kind":"pr","value":"#27903","number":27903},{"kind":"pr","value":"#28304","number":28304}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":27608,"title":"fix(conversation_loop): NameError on rate-limit — _pool_may_recover_from_rate_limit not in scope","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#27370","number":27370}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":27732,"title":"fix(agent): import _pool_may_recover_from_rate_limit in conversation_loop (#27719)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#28345","number":28345},{"kind":"pr","value":"#28159","number":28159},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27465","number":27465},{"kind":"pr","value":"#27468","number":27468},{"kind":"pr","value":"#27583","number":27583},{"kind":"pr","value":"#27686","number":27686},{"kind":"pr","value":"#27732","number":27732},{"kind":"pr","value":"#27734","number":27734},{"kind":"pr","value":"#27735","number":27735},{"kind":"pr","value":"#27750","number":27750},{"kind":"pr","value":"#27891","number":27891},{"kind":"pr","value":"#27903","number":27903},{"kind":"pr","value":"#28304","number":28304}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":27750,"title":"fix(agent): add missing _ra() prefix to pool recovery call","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#28345","number":28345},{"kind":"pr","value":"#28159","number":28159},{"kind":"pr","value":"#27370","number":27370},{"kind":"pr","value":"#27465","number":27465},{"kind":"pr","value":"#27468","number":27468},{"kind":"pr","value":"#27583","number":27583},{"kind":"pr","value":"#27686","number":27686},{"kind":"pr","value":"#27732","number":27732},{"kind":"pr","value":"#27734","number":27734},{"kind":"pr","value":"#27735","number":27735},{"kind":"pr","value":"#27750","number":27750},{"kind":"pr","value":"#27891","number":27891},{"kind":"pr","value":"#27903","number":27903},{"kind":"pr","value":"#28304","number":28304}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":28345,"title":"fix(conversation_loop): _ra()._pool_may_recover_from_rate_limit (NameError) (#28159)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/conversation_loop.py","tests/agent/test_gemini_fast_fallback.py"],"fix_hunks":[{"file":"agent/conversation_loop.py","hunks":["@@ -2332,7 +2332,7 @@ def _stop_spinner():\n                     # still recover.  See _pool_may_recover_from_rate_limit\n                     # for the single-credential-pool and CloudCode-quota\n                     # exceptions.  Fixes #11314 and #13636.\n-                    pool_may_recover = _pool_may_recover_from_rate_limit(\n+                    pool_may_recover = _ra()._pool_may_recover_from_rate_limit(\n                         agent._credential_pool,\n                         provider=agent.provider,\n                         base_url=getattr(agent, \"base_url\", None),"]},{"file":"tests/agent/test_gemini_fast_fallback.py","hunks":["@@ -5,8 +5,10 @@\n Gemini OAuth) the 429 is an account-wide throttle, so waiting for pool\n rotation is pointless — prefer fallback immediately.\n \"\"\"\n+import inspect\n from unittest.mock import MagicMock\n \n+from agent import conversation_loop\n from run_agent import _pool_may_recover_from_rate_limit\n \n ","@@ -60,3 +62,17 @@ def test_exhausted_pool_skips_rotation():\n \n def test_no_pool_skips_rotation():\n     assert _pool_may_recover_from_rate_limit(None) is False\n+\n+\n+def test_conversation_loop_resolves_pool_helper_through_run_agent_module():\n+    \"\"\"Extracted conversation loop must honor tests/patches on run_agent.\n+\n+    conversation_loop intentionally lazy-loads run_agent via _ra().  If this\n+    call site uses a bare imported helper, monkeypatching run_agent in tests (and\n+    production wrappers that patch run_agent) will not propagate into the\n+    extracted loop; older code also hit NameError in this branch.\n+    \"\"\"\n+    source = inspect.getsource(conversation_loop.run_conversation)\n+\n+    assert \"_ra()._pool_may_recover_from_rate_limit(\" in source\n+    assert \"pool_may_recover = _pool_may_recover_from_rate_limit(\" not in source\n"]}]},{"kind":"pr","number":29210,"title":"fix(agent): add _ra() prefix to _pool_may_recover_from_rate_limit call","url":null,"state":"closed","close_reason":{"category":"silent_close","implication":"active"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}