openclaw - ✅(Solved) Fix [Bug]: Bug] WebChat channel sends duplicate messages causing double agent responses [1 pull requests, 1 comments, 2 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#72536Fetched 2026-04-28 06:34:50
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×3commented ×1labeled ×1marked_as_duplicate ×1

WebChat dashboard sends each message twice to the agent, causing duplicate responses and doubled token consumption.

Root Cause

Suspected root cause: WebSocket reconnection logic may not properly clean up old connections, or message dispatching may not deduplicate by message_id.

Fix Action

Fix / Workaround

Suspected root cause: WebSocket reconnection logic may not properly clean up old connections, or message dispatching may not deduplicate by message_id.

PR fix notes

PR #72605: fix(gateway): dedupe WebChat sends before attachment parsing

Description (problem / solution / changelog)

Summary

  • Problem: WebChat / Control UI duplicate chat.send calls with the same message id could race before active run registration when attachment/model preprocessing awaited.
  • Why it matters: the duplicate could dispatch a second agent response, causing duplicate replies and doubled token use.
  • What changed: chat.send now claims the chat:<idempotencyKey> dedupe entry as in_flight before attachment preprocessing, overwrites it on terminal success/error, and keeps agent.wait from treating stale same-runId agent snapshots as terminal while that preclaim is active.
  • What did NOT change (scope boundary): no UI event handling, WebSocket reconnect logic, channel routing, model/provider behavior, or external delivery semantics changed.

AI-assisted: yes. I used Codex to implement and review the change, and I understand the code path and tests.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #72536
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: chat.send only wrote the idempotency dedupe entry after terminal completion/failure, and the active run abort/controller entry was registered after async attachment/model capability preprocessing. A repeated WebChat send with the same idempotency key could enter that await window before the active run existed.
  • Missing detection / guardrail: the existing race test did not assert that the dedupe state was claimed before attachment preprocessing or that stale same-runId agent snapshots were ignored during that preclaimed in-flight state.
  • Contributing context (if known): duplicate WebChat messages use the same message id / idempotency key, so backend idempotency is the narrow guardrail even if the duplicate originates from reconnect/retry behavior.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/gateway/server.chat.gateway-server-chat-b.test.ts
    • src/gateway/server-methods/agent.test.ts
  • Scenario the test should lock in: while the first chat.send is waiting on attachment/model preprocessing, the gateway has already cached {status:"in_flight"}, a duplicate send with the same idempotency key returns in_flight, only one dispatch/run is created, and agent.wait does not resolve from stale same-runId agent terminal snapshots during the preclaimed chat state.
  • Why this is the smallest reliable guardrail: it exercises the gateway handler seam at the exact await boundary that previously allowed the race, without requiring a browser/WebSocket reconnect reproduction.
  • Existing test that already covers this (if any): the attachment race test existed but now asserts the earlier dedupe claim and single dispatch behavior.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

Duplicate WebChat / Control UI sends with the same message id now stay on the in-flight path instead of starting a second agent response.

Diagram (if applicable)

Before:
[first chat.send] -> [attachment/model preprocessing await]
[duplicate chat.send] -> [no dedupe/no active run yet] -> [second dispatch possible]

After:
[first chat.send] -> [chat:<id> in_flight claim] -> [attachment/model preprocessing await]
[duplicate chat.send] -> [cached in_flight] -> [single dispatch]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS / Darwin
  • Runtime/container: local Node/pnpm checkout
  • Model/provider: mocked gateway test provider
  • Integration/channel (if any): WebChat / Control UI chat.send
  • Relevant config (redacted): local test session with test-provider / vision-model

Steps

  1. Start a chat.send with an image attachment and idempotency key idem-attachment-race.
  2. Hold the first request while model catalog / attachment preprocessing is awaiting.
  3. Send a duplicate chat.send with the same idempotency key before abort-controller registration.
  4. Release preprocessing and let the first request dispatch.

Expected

  • The duplicate request returns { runId, status: "in_flight" }.
  • The original request returns { runId, status: "started" }.
  • Only one dispatchInboundMessage and one addChatRun occur.
  • agent.wait ignores stale agent:<runId> terminal snapshots while the preclaimed chat:<runId> entry is in flight.

Actual

  • Matches expected after this patch.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Validation run locally:

pnpm test src/gateway/server-methods/agent.test.ts src/gateway/server.chat.gateway-server-chat-b.test.ts -- --reporter=verbose
✓ src/gateway/server.chat.gateway-server-chat-b.test.ts > gateway server chat > chat.send returns in_flight when duplicate attachment send wins parsing race
✓ src/gateway/server-methods/agent.test.ts > gateway agent handler chat.abort integration > agent.wait ignores stale agent snapshots while chat.send is preclaimed
✓ src/gateway/server-methods/agent.test.ts > gateway agent handler chat.abort integration > dedupe wait ignores stale agent snapshots while chat.send is preclaimed
Test Files 2 passed
Tests 72 passed

Additional local checks:

pnpm check:changed
pnpm build
pnpm check
pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/gateway/server-methods/chat.ts src/gateway/server.chat.gateway-server-chat-b.test.ts
codex review --base upstream/main

pnpm test was also attempted. It currently fails on unrelated existing broad-suite issues outside this patch, including missing generated/public plugin artifacts and strict SSRF/network fixture expectations in provider/browser/media extension tests. The targeted gateway tests above and pnpm check:changed pass for this touched surface.

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: source path from chat.send validation through idempotency claim, attachment preprocessing, abort-controller registration, terminal dedupe overwrite, duplicate response, and agent.wait stale-snapshot handling.
  • Edge cases checked: attachment preprocessing failure overwrites the provisional dedupe entry with an error; successful completion overwrites with ok; concurrent duplicate before controller registration returns in_flight; preclaimed chat dedupe prevents stale same-runId agent terminal snapshots from preempting waits.
  • What you did not verify: no manual browser/WebChat UI reproduction and no live model/provider run.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: a provisional in-flight dedupe entry could make agent.wait prefer the active chat path before the abort controller exists.
    • Mitigation: agent.wait now treats non-terminal chat:<runId> dedupe entries as active chat sends and ignores stale same-runId agent snapshots in that window.
  • Risk: attachment/model preprocessing can fail after the provisional dedupe claim.
    • Mitigation: preprocessing failures overwrite the provisional dedupe entry with the shaped error, and terminal dispatch success/failure still overwrites the same dedupe key.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/gateway/server-methods/agent.test.ts (modified, +52/-0)
  • src/gateway/server-methods/agent.ts (modified, +13/-1)
  • src/gateway/server-methods/chat.ts (modified, +41/-23)
  • src/gateway/server.chat.gateway-server-chat-b.test.ts (modified, +8/-2)

Code Example

WebSocket connection logs show multiple connections:
webchat connected conn=xxx client=openclaw-control-ui webchat v2026.4.24
webchat connected conn=yyy client=openclaw-control-ui webchat v2026.4.24
Port status shows 2 ESTABLISHED connections:
$ lsof -i :18789 | grep ESTABLISHED
node      34082   ac  ...  TCP localhost:18789->localhost:56845 (ESTABLISHED)
com.apple 34160   ac  ...  TCP localhost:56845->localhost:18789 (ESTABLISHED)
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

WebChat dashboard sends each message twice to the agent, causing duplicate responses and doubled token consumption.

Steps to reproduce

  1. Start OpenClaw gateway: openclaw gateway start
  2. Open WebChat Dashboard at http://127.0.0.1:18789
  3. Ensure only ONE browser tab is open
  4. Send any message to the agent
  5. Observe that the agent receives and responds to the same message twice

Expected behavior

Each message sent via WebChat should be delivered exactly once to the agent, regardless of WebSocket connection state.

Actual behavior

he agent receives the same message twice (same message_id), processes it twice, and sends two identical responses. This results in doubled token usage and confusing user experience. evidence from logs shows multiple "webchat connected" entries and lsof shows 2 ESTABLISHED connections on port 18789 even with only one browser tab open.

OpenClaw version

2026.4.24 (cbcfdf6)

Operating system

macOS 15.4 (Darwin 25.4.0, arm64)

Install method

npm install -g openclaw LaunchAgent service

Model

moonshot/kimi-k2.6

Provider / routing chain

Direct provider (no proxy/router)

Additional provider/model setup details

No response

Logs, screenshots, and evidence

WebSocket connection logs show multiple connections:
webchat connected conn=xxx client=openclaw-control-ui webchat v2026.4.24
webchat connected conn=yyy client=openclaw-control-ui webchat v2026.4.24
Port status shows 2 ESTABLISHED connections:
$ lsof -i :18789 | grep ESTABLISHED
node      34082   ac  ...  TCP localhost:18789->localhost:56845 (ESTABLISHED)
com.apple 34160   ac  ...  TCP localhost:56845->localhost:18789 (ESTABLISHED)

Impact and severity

Affected users: All WebChat dashboard users Severity: Annoying / Extra cost Frequency: Always (100% reproducible) Consequence:

  • Doubled token consumption (increased API costs)
  • Confusing user experience (duplicate responses)
  • Agent state pollution (duplicate processing)

Additional information

Tried solutions (not effective):

  • Restarted gateway multiple times
  • Cleared plugin runtime dependencies
  • Ensured only one browser tab open
  • Reinstalled OpenClaw via npm

Suspected root cause: WebSocket reconnection logic may not properly clean up old connections, or message dispatching may not deduplicate by message_id.

extent analysis

TL;DR

The most likely fix involves modifying the WebSocket reconnection logic to properly clean up old connections or implementing message deduplication by message_id.

Guidance

  • Investigate the WebSocket connection establishment and reconnection logic to ensure that old connections are properly closed before new ones are established.
  • Consider implementing a mechanism to deduplicate messages by message_id to prevent duplicate processing and responses.
  • Review the openclaw gateway start command and its configuration to see if there are any options or settings that can help manage WebSocket connections.
  • Verify that the issue persists even when using a different browser or client to rule out client-side issues.

Example

No code snippet is provided as the issue does not contain sufficient information about the specific implementation details.

Notes

The provided information suggests that the issue is related to the WebSocket connection management, but without access to the codebase, it's difficult to provide a more specific solution. The suggested steps are intended to help guide the investigation and potential fix.

Recommendation

Apply workaround: Implement message deduplication by message_id to prevent duplicate processing and responses, as this seems to be a more straightforward solution that can be implemented without modifying the underlying WebSocket connection logic.

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

Each message sent via WebChat should be delivered exactly once to the agent, regardless of WebSocket connection state.

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 - ✅(Solved) Fix [Bug]: Bug] WebChat channel sends duplicate messages causing double agent responses [1 pull requests, 1 comments, 2 participants]