hermes - 💡(How to fix) Fix bug(anthropic_adapter): TOCTOU race in _get_claude_code_version() — concurrent OAuth calls each spawn a subprocess to detect version

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…

agent/anthropic_adapter._get_claude_code_version() (line 317) has a bare if _claude_code_version_cache is None: guard with no lock. Two threads concurrently building OAuth headers can both see _claude_code_version_cache is None and each call _detect_claude_code_version(), which spawns subprocess.run(["claude", "--version"], timeout=5).

_claude_code_version_cache: Optional[str] = None  # no lock

def _get_claude_code_version() -> str:
    global _claude_code_version_cache
    if _claude_code_version_cache is None:                 # ← race
        _claude_code_version_cache = _detect_claude_code_version()  # spawns subprocess
    return _claude_code_version_cache

Root Cause

agent/anthropic_adapter._get_claude_code_version() (line 317) has a bare if _claude_code_version_cache is None: guard with no lock. Two threads concurrently building OAuth headers can both see _claude_code_version_cache is None and each call _detect_claude_code_version(), which spawns subprocess.run(["claude", "--version"], timeout=5).

_claude_code_version_cache: Optional[str] = None  # no lock

def _get_claude_code_version() -> str:
    global _claude_code_version_cache
    if _claude_code_version_cache is None:                 # ← race
        _claude_code_version_cache = _detect_claude_code_version()  # spawns subprocess
    return _claude_code_version_cache

Fix Action

Fix

Add import threading and _claude_code_version_lock = threading.Lock(). Apply double-checked locking. Same pattern as #24731.

Code Example

_claude_code_version_cache: Optional[str] = None  # no lock

def _get_claude_code_version() -> str:
    global _claude_code_version_cache
    if _claude_code_version_cache is None:                 # ← race
        _claude_code_version_cache = _detect_claude_code_version()  # spawns subprocess
    return _claude_code_version_cache
RAW_BUFFERClick to expand / collapse

Summary

agent/anthropic_adapter._get_claude_code_version() (line 317) has a bare if _claude_code_version_cache is None: guard with no lock. Two threads concurrently building OAuth headers can both see _claude_code_version_cache is None and each call _detect_claude_code_version(), which spawns subprocess.run(["claude", "--version"], timeout=5).

_claude_code_version_cache: Optional[str] = None  # no lock

def _get_claude_code_version() -> str:
    global _claude_code_version_cache
    if _claude_code_version_cache is None:                 # ← race
        _claude_code_version_cache = _detect_claude_code_version()  # spawns subprocess
    return _claude_code_version_cache

Impact

  • Performance: _detect_claude_code_version spawns two subprocess.run calls (one for claude, one for claude-code) each with a 5-second timeout. Under concurrent OAuth request bursts, N threads each spawn up to 2 subprocesses unnecessarily.
  • Correctness: All threads would get the same version string (deterministic), so functional impact is low — but the wasted subprocess overhead adds latency to the first authenticated request burst.

Fix

Add import threading and _claude_code_version_lock = threading.Lock(). Apply double-checked locking. Same pattern as #24731.

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 bug(anthropic_adapter): TOCTOU race in _get_claude_code_version() — concurrent OAuth calls each spawn a subprocess to detect version