openclaw - 💡(How to fix) Fix sessions_yield leaves control-ui frozen: WebSocket RPC response routes to wrong connection [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#52248Fetched 2026-04-08 01:13:46
View on GitHub
Comments
2
Participants
3
Timeline
2
Reactions
0
Timeline (top)
commented ×2

Root Cause

The agent.wait RPC handler in channel-web loses the originating WebSocket connection context during async parent-turn execution. When the response is ready, it serializes to a fallback/internal connection instead of routing back to the original client.

Affected file: src/channels/web.ts (compiled to dist/channel-web-CnBLwZJB.js)

Issue: Connection ID not preserved through async parent execution flow.

Fix Action

Workaround

Until this is fixed, avoid sessions_yield in browser UIs. Use polling or callback patterns instead to check for completion.

Code Example

12:31:33 [ws] webchat connected 
  conn=2db72394-b0ac-47f3-97fa-951d7cca6bf6 
  client=openclaw-control-ui

12:32:09 [ws] ⇄ res ✓ agent.wait 13616ms 
  conn=5bc0ff0b-1a77 ← WRONG CONNECTION (ephemeral, not client's)
  id=44a52445-915b

12:32:47 [ws] webchat disconnected code=1001 
  conn=2db72394-b0ac-47f3-97fa-951d7cca6bf6 (timeout after 74 seconds)
RAW_BUFFERClick to expand / collapse

When a WebSocket client (control-ui) calls agent.wait RPC and a parent turn completes, the response is routed to an ephemeral internal connection instead of the original client connection. The browser UI remains frozen/waiting until the user manually refreshes.

Steps to Reproduce

  1. Open control-ui in browser
  2. Execute: sessions_spawn({ runtime: "acp", agentId: "codex", task: "write hello world" })
  3. Execute: sessions_yield()
  4. Wait for ACP child to complete (parent auto-resumes backend)
  5. Observe: Browser control-ui shows no update, remains in "waiting" state
  6. Verify: Browser must be manually refreshed to see results

Expected Behavior

Browser UI automatically updates when parent turn completes. No manual refresh required.

Actual Behavior

Browser UI stays frozen. agent.wait RPC response never reaches the original WebSocket client connection.

Evidence

Gateway Log Analysis

12:31:33 [ws] webchat connected 
  conn=2db72394-b0ac-47f3-97fa-951d7cca6bf6 
  client=openclaw-control-ui

12:32:09 [ws] ⇄ res ✓ agent.wait 13616ms 
  conn=5bc0ff0b-1a77 ← WRONG CONNECTION (ephemeral, not client's)
  id=44a52445-915b

12:32:47 [ws] webchat disconnected code=1001 
  conn=2db72394-b0ac-47f3-97fa-951d7cca6bf6 (timeout after 74 seconds)

Key findings:

  • Client WebSocket: conn=2db72394...6bf6
  • RPC response sent to: conn=5bc0ff0b...1a77different
  • Connection 5bc0ff0b appears only once in entire gateway log (no connect/disconnect events)
  • It's ephemeral/internal, not a real client connection
  • Client connection times out waiting for response, then reconnects (forcing UI refresh)

Root Cause

The agent.wait RPC handler in channel-web loses the originating WebSocket connection context during async parent-turn execution. When the response is ready, it serializes to a fallback/internal connection instead of routing back to the original client.

Affected file: src/channels/web.ts (compiled to dist/channel-web-CnBLwZJB.js)

Issue: Connection ID not preserved through async parent execution flow.

Proposed Fix

In the agent.wait RPC handler:

  1. Capture the originating WebSocket connection ID at RPC method entry
  2. Preserve that connection ID context through async parent-turn execution
  3. Route the completion response back to the captured connection ID, not a fallback connection
  4. Test that completion events reach the original client without requiring manual refresh

System Information

  • OpenClaw: 2026.3.13 (61d171a)
  • Node: 25.8.1
  • OS: macOS
  • Setup: Local gateway on loopback (127.0.0.1:18789)
  • Related patch: Phase A patch deployed (parent auto-resume works at backend level, just blocked by this UI notification bug)

Impact

Severity: Critical

  • Blocks all WebSocket clients calling agent.wait RPC
  • Makes sessions_yield + ACP workflows unusable in browser UI
  • Affects: control-ui, web chat, any browser-based client
  • Workaround: Manual refresh required after every yield (poor UX)

Workaround

Until this is fixed, avoid sessions_yield in browser UIs. Use polling or callback patterns instead to check for completion.

Additional Context

This bug was discovered while testing ACP spawn + yield behavior after deploying a Phase A patch that fixed parent auto-resume at the backend level. The backend execution works correctly; the issue is purely in the WebSocket RPC response routing.

extent analysis

Fix Plan

To fix the issue, we need to preserve the originating WebSocket connection ID through async parent-turn execution. Here are the steps:

  • Capture the originating WebSocket connection ID at RPC method entry:
const connectionId = ctx.websocket.connId;
  • Preserve that connection ID context through async parent-turn execution:
const parentTurnPromise = async () => {
  // ...
  return {
    connectionId, // preserve connectionId
    // ...
  };
};
  • Route the completion response back to the captured connection ID:
const response = {
  // ...
  connectionId, // use preserved connectionId
};

// Send response back to the original client connection
ctx.websocket.send(response);
  • Test that completion events reach the original client without requiring manual refresh:
// Add logging to verify response is sent to correct connection
console.log(`Response sent to connection ${response.connectionId}`);

Code Changes

In src/channels/web.ts, update the agent.wait RPC handler to capture and preserve the connection ID:

import { WebSocketContext } from './websocket-context';

export const agentWaitHandler = async (ctx: WebSocketContext) => {
  const connectionId = ctx.websocket.connId;
  const parentTurnPromise = async () => {
    // ...
    return {
      connectionId, // preserve connectionId
      // ...
    };
  };

  const response = await parentTurnPromise();
  // Send response back to the original client connection
  ctx.websocket.send({
    connectionId, // use preserved connectionId
    // ...
  });
};

Verification

To verify the fix, follow these steps:

  1. Update the agent.wait RPC handler with the new code.
  2. Restart the gateway.
  3. Open control-ui in the browser.
  4. Execute sessions_spawn and sessions_yield.
  5. Wait for the ACP child to complete.
  6. Verify that the browser UI updates automatically without requiring a manual refresh.
  7. Check the gateway logs to ensure the response is sent to the correct connection ID.

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…

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 sessions_yield leaves control-ui frozen: WebSocket RPC response routes to wrong connection [2 comments, 3 participants]