[BUG] --print mode hangs indefinitely when team agents (in_process_teammate) are used

Resolved 💬 1 comment Opened Mar 2, 2026 by freedomhui Closed Mar 5, 2026

Description

When using --print (non-interactive) mode with team agents (TeamCreate + Agent with team_name), the session hangs indefinitely after the lead agent finishes its work. The lead's do-while loop in the print mode controller treats in_process_teammate tasks as blocking background tasks that must complete before exiting, but team agents persist across turns (their status stays "running"), so the loop never terminates.

Steps to Reproduce

claude --print "Create a team with 2 agents, have them each send a broadcast, then shut them all down"

Or programmatically:

claude --print --output-format stream-json \
  "Spawn a team of 2 agents, have a quick standup, then gracefully shutdown all teammates"

Expected Behavior

After all team agents are shut down (via shutdown_requestshutdown_response with approve: true), the --print session should exit cleanly.

Actual Behavior

The session hangs forever. Even after all teammates have shut down and their tasks are marked complete, the do-while loop continues spinning because it checks for any task with status === "running" — and in_process_teammate tasks remain in the running state throughout their lifecycle.

Root Cause

In the print mode controller (ITz / print.ts logic in bundled cli.js), the do-while exit condition checks:

// npm version (minified):
Kv8(K1).some((D1) => lP(D1))

// native binary (less minified):  
function AER(T) {
  for (let R of Object.values(T.tasks))
    if (R.type === "in_process_teammate" && R.status === "running")
      return true;
  return false;
}

This returns true for any running in_process_teammate, which keeps the outer do-while loop spinning. The loop was designed for short-lived background tasks (bash, subagents) that naturally complete, but team agents are long-lived — they stay "running" across wake/idle cycles and only terminate on explicit shutdown.

Suggested Fix

Exclude in_process_teammate tasks from the do-while active-task check:

// Before:
tasks.some((task) => isActive(task))

// After:
tasks.some((task) => isActive(task) && task.type !== "in_process_teammate")

Team agent lifecycle is already managed by the shutdown protocol (shutdown_requestshutdown_response). The do-while loop should only block on tasks that are expected to naturally complete (bash commands, subagents).

Workaround

Patch the npm-distributed cli.js (plain-text JS) to add the in_process_teammate exclusion. This is not possible with the native Bun binary — same-length byte replacement causes the binary to hang on startup due to integrity checks. Details in the patch script below.

<details>
<summary>Patch script (for npm version only)</summary>

CLI_JS="$(npm root -g)/@anthropic-ai/claude-code/cli.js"
cp "$CLI_JS" "$CLI_JS.bak"

node -e "
const fs = require('fs');
let src = fs.readFileSync('$CLI_JS', 'utf8');

// Exclude in_process_teammate from do-while active-task check
const old1 = 'Kv8(K1).some((D1)=>lP(D1))';
const new1 = 'Kv8(K1).some((D1)=>lP(D1)&&D1.type!==\"in_process_teammate\")';
if (src.includes(old1)) {
  src = src.replace(old1, new1);
  fs.writeFileSync('$CLI_JS', src);
  console.log('Patched successfully');
}
"

Note: The minified variable names (Kv8, lP, D1) are version-specific. This patch was tested against v2.1.63.
</details>

Environment

  • Claude Code version: 2.1.63
  • OS: macOS Darwin 25.2.0 (Apple Silicon)
  • Installation: Both native binary (~/.local/share/claude/versions/2.1.63) and npm (@anthropic-ai/claude-code@2.1.63)
  • Mode: --print (non-interactive)

Additional Context

  • The native Bun binary has the same JS logic with different (less minified) variable names, but cannot be patched — even same-length byte replacement causes the binary to fail integrity checks and hang on startup.
  • Related issue: #26426 (inbox polling in non-interactive mode — different code path, same feature area)
  • Two additional improvements were applied alongside the main fix:
  • Poll timeout: 60s safety net to force exit if shutdown stalls
  • Reset the shutdown-injected flag between turns to allow retry

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗