teammateMode "tmux" silently falls back to in-process since ~v2.1.50+
Environment
- Claude Code version: 2.1.62
- OS: macOS Darwin 25.4.0
- tmux: 3.6a
$TMUX: set (/private/tmp/tmux-501/default,30374,3)- Config:
"teammateMode": "tmux"in~/.claude/settings.json CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:1
Bug Description
All teammates spawn with backendType: "in-process" and no tmux panes are created, despite "teammateMode": "tmux" being explicitly set in settings. This worked correctly on earlier versions (~Feb 8-9, 2026) but broke silently around Feb 19.
Expected Behavior
With "teammateMode": "tmux", agents should spawn in tmux split-panes (or new windows), producing real pane IDs like %1, %8, %25, etc.
Actual Behavior
All agents spawn in-process. Team config files show "tmuxPaneId": "in-process" and "backendType": "in-process" for every teammate. No tmux panes are created.
Evidence
Working teams (Feb 8-9, earlier Claude Code version):
| Team | Pane IDs | Backend |
|------|----------|---------|
| tidy-conjuring-axolotl | %1, %2, %3 | tmux |
| fluffy-wishing-duckling | %6, %7 | tmux |
| game-polish | %8 | tmux |
| word-survivor-audit | %9, %16 | tmux |
| word-survivor-v2 | %25 | tmux |
| v2-playable | %39 | tmux |
Broken teams (Feb 19+, Claude Code ~2.1.50+):
| Team | Pane IDs | Backend |
|------|----------|---------|
| sim-fix | all in-process | in-process |
| animation-prototypes | all in-process | in-process |
| delightful-v2 | all in-process | in-process |
| polymarket-refactor | all in-process | in-process |
| great | all in-process | in-process |
| life-audit | all in-process | in-process |
| motion-system | all in-process | in-process |
Root Cause Analysis
From the compiled binary, the teammate spawn decision flows through a gatekeeper function (deobfuscated as cv() / isInProcessEnabled):
// Decision function — called before any spawn
function cv() {
// CHECK 1: Non-interactive session (process.stdin.isTTY)
if (F_()) // ← If stdin is not a TTY, returns true immediately
return true; // → FORCE IN-PROCESS, skip config check
// CHECK 2: Read teammateMode from config
let T = U28(); // reads captured teammate mode
if (T === "in-process") return true;
else if (T === "tmux") return false;
else /* "auto" */ return !WwR();
}
The spawn entry point:
async function k28(T, R) {
if (cv()) // ← If true, config is never consulted
return b28(T, R); // → in-process spawn (hardcoded)
if (T.use_splitpane !== false)
return P28(T, R); // → tmux split-pane spawn
return y28(T, R); // → tmux new-window spawn
}
The problem: The non-interactive TTY check (F_(), which checks process.stdin.isTTY) runs before the config check. If process.stdin.isTTY returns false (even inside a real terminal session), it short-circuits to in-process mode and the "teammateMode": "tmux" setting is never consulted.
The binary contains the log message "[BackendRegistry] isInProcessEnabled: true (non-interactive session)" on this code path, confirming this is the bypass route.
Suggested Fix
- When
teammateModeis explicitly"tmux", the non-interactive TTY check should not override it. The user has explicitly opted in to tmux mode — that should be respected regardless of TTY state. - If a fallback from tmux to in-process does occur, emit a visible warning/log rather than silently degrading. Something like:
"[BackendRegistry] Warning: teammateMode is 'tmux' but falling back to in-process because stdin is not a TTY"
Reproduction
- Be inside a tmux session with
$TMUXset - Set
"teammateMode": "tmux"in~/.claude/settings.json - Set
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 - Create a team and spawn teammates
- Check
~/.claude/teams/<team-name>/config.json— all members havebackendType: "in-process"instead of"tmux"
This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗