-
Notifications
You must be signed in to change notification settings - Fork 1
Bug: daemon caches resolved binary path across Claude upgrades (stale symlink) #120
Description
What happened
After upgrading Claude Code from 2.1.52 to 2.1.70, the agentctl daemon kept trying to spawn /Users/ms/.local/share/claude/versions/2.1.52 — the path it resolved at startup. New session launches failed with:
[claude-code] spawn error: spawn /Users/ms/.local/share/claude/versions/2.1.52 ENOENT
The symlink at ~/.local/bin/claude correctly pointed to 2.1.70, but the daemon's in-memory cache (resolvedCache in resolve-binary.ts) held the old resolved realpath from when it first started.
What was expected
The daemon should either:
- Clear the binary cache on each launch (not just daemon restart), or
- Re-resolve symlinks periodically (e.g., on spawn failure), or
- Not resolve through symlinks at all — spawn the symlink path directly and let the OS resolve it
How to reproduce
- Start agentctl daemon with Claude Code 2.1.52 installed
- Upgrade Claude Code to 2.1.70 (symlink updates, old binary may or may not remain)
- Try
agentctl launch claude-code— fails with ENOENT for old version path
Workaround
agentctl daemon restart clears the in-memory cache and re-resolves to the current symlink target.
Root cause
src/utils/resolve-binary.ts calls fs.realpath() to resolve symlinks, then caches the result in a Map for the daemon's lifetime. The cache is never invalidated except on process restart. clearBinaryCache() exists but is never called.
Proposed fix
Option A (simplest): On ENOENT spawn error, call clearBinaryCache() and retry once.
Option B: Don't realpath() — just verify the symlink is executable and spawn it directly.
Option C: Call clearBinaryCache() before each launch.
Environment
- agentctl: latest (linked from ~/personal/agentctl)
- macOS Darwin 25.2.0 arm64
- Claude Code upgraded 2.1.52 → 2.1.70