hermes - 💡(How to fix) Fix _last_resolved_tool_names global causes race condition in concurrent sessions

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Fix Action

Fix / Workaround

Existing Mitigation (Insufficient)

Code Example

# model_tools.py:212
_last_resolved_tool_names: list = []  # process-global!

---

_last_resolved_tool_names: ContextVar[list] = ContextVar("_last_resolved_tool_names", default=[])
RAW_BUFFERClick to expand / collapse

Bug: _last_resolved_tool_names Global Race Condition

Severity: HIGH
File: model_tools.py:212
Affected versions: v0.14.0 (and likely all prior)

Problem

_last_resolved_tool_names is a module-level List[str] — not per-session, not per-agent-instance. When two sessions run concurrently in the same gateway process (e.g., two Feishu/Telegram users messaging simultaneously from different chats), each session's get_tool_definitions() overwrites this global:

# model_tools.py:212
_last_resolved_tool_names: list = []  # process-global!

Race scenario:

  1. Session A calls get_tool_definitions() → sets _last_resolved_tool_names = [A's tools]
  2. Session B calls get_tool_definitions() → overwrites _last_resolved_tool_names = [B's tools]
  3. Session A calls handle_function_call("terminal") → checks B's tool list → may reject A's tool call as "not available"

Existing Mitigation (Insufficient)

delegate_tool.py saves and restores _last_resolved_tool_names around subagent execution, but this only covers the delegation chain within a single session — not concurrent top-level sessions.

Impact

  • Tool calls may be incorrectly rejected with "tool not available" errors
  • The race window is small (tool list is set at run_conversation() start and typically stable), but real
  • More likely when sessions have different toolsets enabled

Suggested Fixes

Option A (Best): Convert to ContextVar[List[str]] — each asyncio task gets automatic isolation:

_last_resolved_tool_names: ContextVar[list] = ContextVar("_last_resolved_tool_names", default=[])

Option B: Store as per-AIAgent instance attribute (self._resolved_tool_names) and pass through handle_function_call().

Option C: Convert to Dict[str, List[str]] keyed by session_key.

Reproduction

  1. Start gateway with a messaging platform (Feishu/Telegram/Discord)
  2. Two users send messages simultaneously from different chats with different toolset configurations
  3. Check logs for spurious "tool not available" rejections

Environment

  • Hermes Agent v0.14.0 (tag v2026.5.16)
  • 10 profiles running concurrently
  • Feishu WebSocket platform

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix _last_resolved_tool_names global causes race condition in concurrent sessions