Skip to content

Bug: native subagent completion output not delivered to parent agent (delivery_status stuck on 'pending') #78656

@moeedahmed

Description

@moeedahmed

Bug Description

Native subagent runs (via sessions_spawn runtime=subagent) complete successfully, but the parent agent never receives the child's output. The delivery_status field in task_runs remains pending indefinitely, and agent.wait on the child session returns status=timeout with empty output.

Root Cause

Two separate failure points in the completion/capture/delivery pipeline:

Failure Point 1 — Result not captured before session cleanup (Hermes, fixed)

File: dist/subagent-registry-CdtzK4_R.js

When a subagent run completes, the wait.resultText (the assistant's final output text) exists only transiently in the child session state before that session store is cleaned up. The parent-side completion capture fired before the result was frozen, causing the delivery payload to be empty.

Fix: In freezeRunResultAtCompletion (called from the announce flow), capture wait.resultText durably before session cleanup. Added caching of assistant text by runId in server-methods-Dvr1K7zh.js and preservation of resultText/replyText in normalized wait results in run-wait-cmY6eTHK.js.

Failure Point 2 — Completed runs incorrectly identified as orphans (Operator, fixed)

File: dist/subagent-registry-CdtzK4_R.js

The function resolveSubagentRunOrphanReason returns "missing-session-entry" when the session store entry for a completed child run is missing. However, for completed runs (those with endedAt set), the session store is expected to be cleaned up after the run ends — this is normal cleanup, not an orphan condition. Only active/unended runs with missing session entries should be pruned.

Fix: In resolveSubagentRunOrphanReason, if sessionEntry is null AND entry.endedAt is set, return null (not an orphan) instead of "missing-session-entry":

// Before:
if (!sessionEntry) return "missing-session-entry";

// After:
if (!sessionEntry) {
    // Session store gone after run ended — expected cleanup, not an orphan.
    // prune only active/unended runs whose store is missing.
    if (typeof params.entry.endedAt === "number") return null;
    return "missing-session-entry";
}

Files Modified (dist patches — for reference only)

These are compiled/bundled dist files and not meant to be edited directly. The patches below are provided as reference implementations — they demonstrate the fix but should ideally be applied at source level and rebuilt.

File Change Applied by
subagent-registry-CdtzK4_R.js Freeze resultText + orphan check fix Hermes + Operator
server-methods-Dvr1K7zh.js Cache assistant text by runId Hermes
run-wait-cmY6eTHK.js Preserve resultText/replyText in normalized wait Hermes
agent-runner.runtime-CjYlXxbm.js Emit CLI final text as resultText on lifecycle end Hermes

Verification

Smoke test with marker SUBAGENT_E2E_FIXED_173504:

task_runs row: cc24b894-d70b-46aa-97ee-1a0f3b27417a
status: succeeded, delivery_status: delivered
agent.wait: status=ok (not timeout)
parent received marker as non-empty output ✓

DB Cleanup

5 stale rows with runtime=subagent, status=succeeded, delivery_status=pending (orphaned before fix) were updated to delivery_status=delivered.

Environment

  • OpenClaw 2026.5.5 (macOS Darwin, node v22.22.1)
  • Runtime: native subagent via sessions_spawn runtime=subagent
  • Task DB: ~/.openclaw/tasks/runs.sqlite

Expected Behavior (after fix)

  1. Child subagent run completes → status=succeeded
  2. delivery_status transitions to delivered (not stuck at pending)
  3. Parent agent's agent.wait returns status=ok with the child's output text
  4. No orphan pruning of completed runs at restore time

Suggested Source-Level Fix

For the orphan-check issue specifically, the fix in resolveSubagentRunOrphanReason is a one-liner at the session-entry null check — add the endedAt guard to return null for completed runs. For the result capture issue, the proper fix requires ensuring wait.resultText is captured and frozen before session cleanup in the lifecycle flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions