openclaw - ✅(Solved) Fix Unhandled rejection: Agent listener invoked outside active run (background exec) [1 pull requests, 2 comments, 3 participants]

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…
GitHub stats
openclaw/openclaw#61592Fetched 2026-04-08 02:57:00
View on GitHub
Comments
2
Participants
3
Timeline
13
Reactions
0
Timeline (top)
referenced ×9commented ×2closed ×1cross-referenced ×1

Error Message

Unhandled promise rejection: Error: Agent listener invoked outside active run
    at Agent.processEvents (pi-agent-core/src/agent.ts:533:10)
    at pi-agent-core/src/agent.ts:380:21
    at Object.onUpdate (pi-agent-core/src/agent-loop.ts:539:7)
    at emitUpdate (exec-defaults-BjdtpCtu.js:1524:8)
    at handleStdout (exec-defaults-BjdtpCtu.js:1546:4)
    at Object.onSupervisorStdout (exec-defaults-BjdtpCtu.js:1610:3)

Root Cause

  1. Agent run starts a long-running background exec (e.g. curl ... | sudo sh to install software)
  2. Agent run completes and replies to user
  3. Background process continues emitting stdout (download progress)
  4. Stdout handler calls emitUpdateAgent.processEvents → throws because no active run

Fix Action

Fixed

PR fix notes

PR #61627: fix(exec): stop emitting tool updates after session is backgrounded

Description (problem / solution / changelog)

Summary

  • Problem: When a backgrounded exec session (background: true) produces stdout output after the owning agent run has finished, the emitUpdate() closure in src/agents/bash-tools.exec-runtime.ts (line 587) unconditionally invokes opts.onUpdate(), delivering a tool_execution_update event to a listener that no longer has an active run. This causes an unhandled rejection (Agent listener invoked outside active run) that crashes the gateway process, terminating all active sessions, cron jobs, and pending interactions.

  • Root Cause: emitUpdate() captures opts.onUpdate in a closure shared with handleStdout() and handleStderr(). When the exec tool yields (via markBackgrounded() at line 1609/1622 of bash-tools.exec.ts), the tool-call promise resolves and the agent run ends, but the process supervisor continues to deliver stdout chunks to handleStdout(), which calls emitUpdate()opts.onUpdate(). The callback's contract requires an active tool execution context, which no longer exists after backgrounding.

  • Fix: Add a session.backgrounded || session.exited guard at the top of emitUpdate(). When either flag is true, the function returns early without invoking opts.onUpdate(). Output data is still captured via appendOutput() (called before emitUpdate() in both handleStdout and handleStderr), so no data is lost — users can retrieve it through process poll/log commands. The session.exited check is included as a belt-and-suspenders measure for the (currently theoretical) race where exit completes before the next event-loop tick processes the backgrounding flag.

  • What changed:

    • src/agents/bash-tools.exec-runtime.ts: Added session.backgrounded || session.exited early-return guard in emitUpdate() (10 new lines including comments)
    • src/agents/bash-tools.test.ts: Added describe("exec backgrounded onUpdate suppression") with one test case that verifies onUpdate is not called after backgrounding (37 new lines)
  • What did NOT change (scope boundary):

    • handleStdout() / handleStderr() still call appendOutput() unconditionally — output capture is unaffected
    • maybeNotifyOnExit() is unaffected — system event notifications for backgrounded exits still work
    • bash-tools.exec.ts (the tool definition layer) is untouched — yield/background logic is unchanged
    • bash-tools.exec-host-gateway.ts does not pass onUpdate to runExecProcess(), so it is unaffected
    • No changes to the agent loop, event handler, or session lifecycle code

Reproduction

  1. Configure a channel with an LLM that uses exec with background: true
  2. Have the background command produce stdout output after the agent run completes (e.g., a build script, a long-running curl)
  3. Observe gateway crash with: Unhandled rejection: Agent listener invoked outside active run
  4. All active sessions, cron jobs, and pending interactions are terminated

Risk / Mitigation

  • Risk: Suppressing onUpdate after backgrounding could theoretically hide output from a UI that expects streaming updates for backgrounded sessions. However, no such UI path exists — once a session is backgrounded, the tool-call has already resolved and the agent has moved on. The process poll/log commands are the designed mechanism for retrieving backgrounded output.
  • Mitigation: The new test case directly verifies the fix by asserting that onUpdate call count does not increase after backgrounding. Existing tests for background exec behavior (bash-tools.test.ts backgrounding suite, bash-tools.exec.background-abort.test.ts) continue to pass, confirming no regression in yield, timeout, or abort semantics.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway
  • Agents
  • Exec / Process management

Linked Issue/PR

Fixes #61592

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/bash-tools.exec-runtime.ts (modified, +3/-0)
  • src/agents/bash-tools.test.ts (modified, +31/-0)

Code Example

Unhandled promise rejection: Error: Agent listener invoked outside active run
    at Agent.processEvents (pi-agent-core/src/agent.ts:533:10)
    at pi-agent-core/src/agent.ts:380:21
    at Object.onUpdate (pi-agent-core/src/agent-loop.ts:539:7)
    at emitUpdate (exec-defaults-BjdtpCtu.js:1524:8)
    at handleStdout (exec-defaults-BjdtpCtu.js:1546:4)
    at Object.onSupervisorStdout (exec-defaults-BjdtpCtu.js:1610:3)
RAW_BUFFERClick to expand / collapse

Bug

Gateway crashes with an unhandled promise rejection when a background exec process emits output after the agent run that spawned it has already completed.

Error

Unhandled promise rejection: Error: Agent listener invoked outside active run
    at Agent.processEvents (pi-agent-core/src/agent.ts:533:10)
    at pi-agent-core/src/agent.ts:380:21
    at Object.onUpdate (pi-agent-core/src/agent-loop.ts:539:7)
    at emitUpdate (exec-defaults-BjdtpCtu.js:1524:8)
    at handleStdout (exec-defaults-BjdtpCtu.js:1546:4)
    at Object.onSupervisorStdout (exec-defaults-BjdtpCtu.js:1610:3)

Reproduction

  1. Agent run starts a long-running background exec (e.g. curl ... | sudo sh to install software)
  2. Agent run completes and replies to user
  3. Background process continues emitting stdout (download progress)
  4. Stdout handler calls emitUpdateAgent.processEvents → throws because no active run

Expected behavior

Background exec output after the spawning run completes should either be silently buffered or ignored, not crash the gateway.

Impact

Gateway process exits, requiring manual restart. Any ongoing conversations and cron jobs are interrupted.

Environment

  • OpenClaw 2026.4.5, running from source on Linux
  • Triggered by exec tool with background: true and timeout: 300
  • Crash timestamp: 2026-04-06T09:10:54+08:00

extent analysis

TL;DR

Modify the Agent.processEvents function to handle events from completed runs by either ignoring or buffering them to prevent unhandled promise rejections.

Guidance

  • Review the Agent.processEvents function in pi-agent-core/src/agent.ts to understand how it handles events from background exec processes after the agent run has completed.
  • Consider adding a check to ignore events from completed runs or buffer them to prevent the gateway from crashing.
  • Investigate the emitUpdate function in exec-defaults-BjdtpCtu.js to see if it can be modified to handle the case where the agent run has already completed.
  • Verify that the background and timeout options for the exec tool are correctly configured to prevent the background process from emitting output after the agent run has completed.

Example

// pi-agent-core/src/agent.ts
class Agent {
  // ...
  processEvents(events) {
    if (!this.isActiveRun()) { // Check if the run is active
      // Ignore or buffer events from completed runs
      return;
    }
    // ...
  }
  // ...
}

Notes

The provided solution assumes that the issue is caused by the Agent.processEvents function not handling events from completed runs correctly. However, without more information about the pi-agent-core and exec-defaults-BjdtpCtu.js modules, it is difficult to provide a more specific solution.

Recommendation

Apply a workaround by modifying the Agent.processEvents function to handle events from completed runs, as this is the most likely cause of the issue and can be implemented without requiring significant changes to the existing codebase.

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…

FAQ

Expected behavior

Background exec output after the spawning run completes should either be silently buffered or ignored, not crash the gateway.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING