openclaw - 💡(How to fix) Fix [Bug]: ACP child sessions report stopReason="stop" with usage=0 before harness completes, causing parent agents to abandon mid-flight [1 participants]

Official PRs (…)
ON THIS PAGE

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#72080Fetched 2026-04-27 05:35:09
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
labeled ×2subscribed ×1

Parent agents that spawn ACP children via sessions_spawn({runtime:"acp"}) and yield with sessions_yield receive a "Background task done" notification ~2 minutes before the underlying harness has actually finished its work, causing the parent to abandon the spawn while the harness keeps running, leaving partial work uncommitted and locks orphaned.

Root Cause

Parent agents that spawn ACP children via sessions_spawn({runtime:"acp"}) and yield with sessions_yield receive a "Background task done" notification ~2 minutes before the underlying harness has actually finished its work, causing the parent to abandon the spawn while the harness keeps running, leaving partial work uncommitted and locks orphaned.

Fix Action

Fix / Workaround

Local workaround applied: replaced the sessions_yield pattern with fire-and-forget spawn + a lock-staleness guard in the workspace's heartbeat protocol (treat lock older than 60 min with no matching commit as orphaned, clear and continue).

Code Example

{
  "type": "message",
  "timestamp": "2026-04-25T19:58:38.453Z",
  "message": {
    "role": "assistant",
    "content": [{ "type": "text", "text": "I'll start by reading the required context files...Now let me explore the existing recommendation and reporting code...Excellent. Let me read the key files I'll be modifying." }],
    "api": "openai-responses",
    "provider": "openclaw",
    "model": "acp-runtime",
    "usage": { "input": 0, "output": 0, "totalTokens": 0 },
    "stopReason": "stop"
  }
}

---

OpenClaw ACP proxy session record: included in Actual behavior above (full JSON of the single recorded assistant message).

Cross-reference timestamps from harness session transcript (`~/.claude/projects/<project>/<sessionId>.jsonl`): included in Actual behavior above as the timeline table.

Same `stopReason: "stop"` + `usage: 0` placeholder shape was found in proxy logs for ACP children that did succeed (e.g. HB-045 in the same loop). The shape itself appears to be written for every ACP turn, regardless of harness completion state — only timing determines whether the harness happens to finish before the parent reads the placeholder as terminal.
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Parent agents that spawn ACP children via sessions_spawn({runtime:"acp"}) and yield with sessions_yield receive a "Background task done" notification ~2 minutes before the underlying harness has actually finished its work, causing the parent to abandon the spawn while the harness keeps running, leaving partial work uncommitted and locks orphaned.

Steps to reproduce

  1. Configure a heartbeat-driven loop where a main agent (openai-codex/gpt-5.4) delegates coding work to a Claude Code ACP child via sessions_spawn({ runtime: "acp", agentId: "claude", mode: "session", thread: true }) followed by sessions_yield.
  2. Trigger a heartbeat that picks a non-trivial coding ticket whose harness work takes >2 minutes.
  3. Observe the parent receive a Background task done system event ~2 minutes after sessions_spawn.
  4. Observe the underlying harness continuing to issue tool calls (Bash/Edit/Write) for several more minutes in its own session transcript at ~/.claude/projects/<project>/<sessionId>.jsonl.

Expected behavior

The parent should be woken when the ACP child actually completes — i.e. a real stopReason accompanied by non-zero token usage, or an explicit ACP session-completion signal. Empirically, the same workflow with an ACP child that happens to finish quickly (~1 minute, e.g. HB-045 in the same loop) reports completion aligned with the harness stopping; only longer-running children are falsely flagged complete early.

Actual behavior

Parent receives Background task done while the harness is still actively making tool calls. OpenClaw's proxy log of the ACP child contains a single assistant message recorded immediately after spawn with stopReason: "stop" and zero token usage:

{
  "type": "message",
  "timestamp": "2026-04-25T19:58:38.453Z",
  "message": {
    "role": "assistant",
    "content": [{ "type": "text", "text": "I'll start by reading the required context files...Now let me explore the existing recommendation and reporting code...Excellent. Let me read the key files I'll be modifying." }],
    "api": "openai-responses",
    "provider": "openclaw",
    "model": "acp-runtime",
    "usage": { "input": 0, "output": 0, "totalTokens": 0 },
    "stopReason": "stop"
  }
}

Notable: provider: "openclaw", model: "acp-runtime" (i.e. the proxy/scaffolding layer, not a real harness response), usage is all zeros, stopReason: "stop" is set on what is clearly only a streaming preview chunk (no tool calls yet).

Cross-referenced timestamps (one observed pass, HB-046):

Local time (Europe/Berlin)SourceEvent
21:56:32Main agent (openai-codex/gpt-5.4)sessions_yield
21:58:38OpenClaw ACP proxy logstopReason: "stop", usage 0
21:58:39Main agentwakes on Background task done, reports failure
21:59:53Claude Code (harness)Bash: check Next.js version
22:00:08Claude CodeEdit: route.ts
22:00:13Claude CodeTodoWrite: 2 of 7 todos completed
22:00:17Claude CodeEdit: RecommendationsPanel.tsx
22:01:26Claude CodeLast write — session ends with last-prompt

The parent woke ~3 minutes before the harness finished. By the time the harness reached its last write, no parent was listening. The harness's TodoWrite shows 4 of 7 todos still pending at session end (including "commit" and "release lock"), so locks/state were never released.

OpenClaw version

2026.4.23

Operating system

macOS 26.1 (Darwin 25.1.0, arm64)

Install method

npm global

Model

openai-codex/gpt-5.4

Provider / routing chain

Main agent: openclaw -> openai-codex (ChatGPT Pro OAuth) ACP child: openclaw -> bundled acpx runtime -> claude CLI -> claude.ai OAuth (Claude Max)

Additional provider/model setup details

Two-tier topology, each side on its own subscription:

  • Main agent (orchestrator) runs on ChatGPT Pro via the openai-codex provider, model openai-codex/gpt-5.4. Reads heartbeats, picks tickets, decides what to do, delegates coding work.
  • Coding agent (delegate) is Claude Code on a Claude Max subscription, spawned through ACP (runtime: "acp", agentId: "claude") with the claude CLI binary as the harness. Does the actual file edits, runs tests, commits.

Bug appears at the boundary: OpenClaw's view of the ACP child reports the harness as stopped while the harness is still mid-flight.

Logs, screenshots, and evidence

OpenClaw ACP proxy session record: included in Actual behavior above (full JSON of the single recorded assistant message).

Cross-reference timestamps from harness session transcript (`~/.claude/projects/<project>/<sessionId>.jsonl`): included in Actual behavior above as the timeline table.

Same `stopReason: "stop"` + `usage: 0` placeholder shape was found in proxy logs for ACP children that did succeed (e.g. HB-045 in the same loop). The shape itself appears to be written for every ACP turn, regardless of harness completion state — only timing determines whether the harness happens to finish before the parent reads the placeholder as terminal.

Impact and severity

Affected: Any workflow using sessions_spawn({runtime: "acp"}) followed by sessions_yield with an underlying harness whose work takes longer than the parent's wake latency. Particularly heartbeat/cron-driven autonomous loops that delegate across a model boundary. Severity: High — silently abandons real work and orphans state. Frequency: 1/1 on observed long-running ACP children. Short-running children evade the symptom by happening to complete before the parent acts on the false-completion signal. Consequence: Files written by the harness sit uncommitted in the working tree; locks/state never released; subsequent heartbeat passes see orphaned state and exit quietly, blocking the loop indefinitely until a human intervenes.

Additional information

Code paths potentially involved (not yet root-caused):

  • extensions/acpx/ — does the runtime emit a synthetic stopReason: "stop" upon receiving the harness's first text chunk, before any real ACP completion signal?
  • src/tasks/task-executor-policy.ts — line 42 emits the Background task done notification.

Local workaround applied: replaced the sessions_yield pattern with fire-and-forget spawn + a lock-staleness guard in the workspace's heartbeat protocol (treat lock older than 60 min with no matching commit as orphaned, clear and continue).

Happy to provide additional logs (full proxy-log JSONL, full harness JSONL, full gateway log slice for the affected window) on request — kept off the public report to avoid leaking unrelated content.

extent analysis

TL;DR

The parent agent is receiving a premature "Background task done" notification, causing it to abandon the spawn while the harness is still running, which can be mitigated by modifying the sessions_yield pattern or adjusting the task executor policy.

Guidance

  • Investigate the extensions/acpx/ code path to determine if the runtime emits a synthetic stopReason: "stop" upon receiving the harness's first text chunk, which may be causing the premature notification.
  • Review the src/tasks/task-executor-policy.ts file, specifically line 42, to understand how the Background task done notification is emitted and if it can be adjusted to wait for the actual completion of the harness.
  • Consider implementing a lock-staleness guard in the workspace's heartbeat protocol to detect and clear orphaned locks, as done in the local workaround.
  • Analyze the OpenClaw ACP proxy log and harness session transcript to better understand the timing and sequence of events leading to the premature notification.

Example

No code snippet is provided as the issue requires further investigation into the specific code paths involved.

Notes

The issue appears to be related to the interaction between the parent agent, the ACP child, and the harness, and may require adjustments to the task executor policy or the runtime's emission of completion signals. The local workaround suggests that modifying the sessions_yield pattern or implementing a lock-staleness guard can mitigate the issue.

Recommendation

Apply a workaround by replacing the sessions_yield pattern with fire-and-forget spawn and implementing a lock-staleness guard in the workspace's heartbeat protocol, as this has been shown to mitigate the issue in the local workaround.

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

The parent should be woken when the ACP child actually completes — i.e. a real stopReason accompanied by non-zero token usage, or an explicit ACP session-completion signal. Empirically, the same workflow with an ACP child that happens to finish quickly (~1 minute, e.g. HB-045 in the same loop) reports completion aligned with the harness stopping; only longer-running children are falsely flagged complete early.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: ACP child sessions report stopReason="stop" with usage=0 before harness completes, causing parent agents to abandon mid-flight [1 participants]