Skip to content

Subagent processes fail to exit — IntercomClient keeps event loop alive #10

@ulusoyomer

Description

@ulusoyomer

Bug: Subagent processes hang after completing their task

Environment

  • pi-subagents: 0.19.3
  • pi-intercom: 0.2.1
  • Node: v24.12.0
  • OS: macOS (Darwin)

Description

When pi-intercom is installed and active, all subagent processes (single, parallel, chain) complete their assigned task successfully but fail to exit within the 5-second timeout. The parent (pi-subagents) then force-terminates them, marking the result as failed — even though the subagent produced the correct output.

FAILED (exit code 1): Subagent process did not exit within 5000ms after its final message. Forcing termination.

Root Cause

The IntercomClient in broker/client.ts connects to the local broker via IPC socket. When the subagent finishes its work and sends its final message, the Node.js event loop does not become empty because:

  1. Open socket connections — The IPC socket to the broker keeps a reference alive
  2. Pending timerssetTimeout/setInterval calls in the client (e.g., reconnect timers, pending ask timeouts) prevent the event loop from draining

As a result, process.exit() is never called naturally, and pi-subagents kills the process after 5 seconds.

Reproduction

  1. Install pi-intercom: pi install npm:pi-intercom
  2. Run any subagent task:
    subagent({ agent: "delegate", task: "Say hello" })
    
  3. Observe: task completes correctly, but process is force-killed after 5s timeout

Impact

  • Single agent: Sometimes succeeds (race condition on event loop), sometimes fails
  • Parallel agents: Always fails (2/2 timeout)
  • Chain: Always fails

Workaround

Remove pi-intercom from settings.json packages, or ensure no symlink exists at ~/.pi/agent/extensions/pi-intercom. This disables intercom bridge but allows subagents to function correctly.

Suggested Fix

The pi-intercom extension should detect when it is running inside a subagent child process and either:

  1. Skip broker connection entirely in subagent mode (check for an env var like PI_SUBAGENT=1 that pi-subagents sets)
  2. Register a cleanup handler that disconnects the client and clears all timers when the subagent signals completion (e.g., listen for a disconnect or beforeExit event)
  3. Use process.disconnect() + unref() on the socket so it does not keep the event loop alive

Option 1 is the simplest and most robust — subagents spawned by pi-subagents do not need intercom broker connectivity since the parent orchestrates them.

Related

  • pi-subagents doctor reports: bridge: inactive (when symlink absent) → subagents work; bridge: active (when symlink present) → subagents hang
  • The intercom tool registration also causes a conflict when the extension is loaded both from npm package and the extensions symlink: Tool "intercom" conflicts with /path/to/pi-intercom/index.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions