openclaw - ✅(Solved) Fix WebSocket delta corruption with inline directive tags [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#66497Fetched 2026-04-15 06:25:57
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1mentioned ×1subscribed ×1

Root Cause

Leading whitespace mismatch:

  1. Pi embedded (normalizeDirectiveWhitespace): ✅ Trims leading/trailing whitespace

    // src/utils/directive-tags.ts:40-65
    const normalized = masked
      .replace(/^\n+/, "")
      .replace(/^[ \t](?=\S)/, "")  // Removes leading space
      .trimEnd();
  2. Gateway (stripInlineDirectiveTagsForDisplay in emitChatDelta): ❌ Does NOT trim

    // src/gateway/server-chat.ts:691-693
    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;  // ❌ No .trim()
    const cleanedDelta = typeof delta === "string" 
      ? stripInlineDirectiveTagsForDisplay(delta).text  // ❌ No .trim()
      : "";
  3. Consequence: previousText.startsWith(nextText) fails → appendUniqueSuffix triggers false overlap detection → character truncation

Fix Action

Fixed

PR fix notes

PR #66499: fix(gateway): WebSocket delta corruption with inline directive tags

Description (problem / solution / changelog)

GitHub Pull Request

Title: fix(gateway): WebSocket delta corruption with inline directive tags

Base branch: main
Compare branch: fix/websocket-delta-corruption-whitespace

Labels: bug, gateway, websocket


Summary

Fixes data corruption in WebSocket delta and final messages when inline directive tags (e.g., [[reply_to_current]], [[audio_as_voice]]) appear at the beginning of LLM output.

Problem

Characters are truncated in WebSocket messages:

  • www.6.xumum.xyzwww.6.xum.xyz (missing um)
  • GOOGLEGOGLE (missing O)

Root Cause

Leading whitespace mismatch between Pi embedded and Gateway processing:

  • Pi embedded (normalizeDirectiveWhitespace): ✅ Trims leading/trailing whitespace
  • Gateway (stripInlineDirectiveTagsForDisplay in emitChatDelta): ❌ Does NOT trim

This mismatch causes:

  1. previousText.startsWith(nextText) check to fail (due to leading space difference)
  2. appendUniqueSuffix to trigger false overlap detection
  3. Character truncation in the final output

Solution

Add .trim() to cleanedText and cleanedDelta in emitChatDelta to align with other code paths:

--- a/src/gateway/server-chat.ts
+++ b/src/gateway/server-chat.ts
@@ -688,9 +688,9 @@ export function createAgentEventHandler({
     text: string,
     delta?: unknown,
   ) => {
-    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;
+    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text.trim();
     const cleanedDelta =
-      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text : "";
+      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text.trim() : "";
     const previousRawText = chatRunState.rawBuffers.get(clientRunId) ?? "";

Changes

Files Modified

  • src/gateway/server-chat.ts - Added .trim() to cleanedText and cleanedDelta (lines 691, 693)

Files Added

  • src/gateway/server-chat-delta-corruption.test.ts - Regression tests covering:
    • [[reply_to_current]] tag
    • [[audio_as_voice]] tag
    • Multiple directive tags
    • Plain text without tags
    • Delta parameter handling

Benefits

  • ✅ Aligns with existing patterns in session-utils.fs.ts and Discord extension
  • ✅ Fixes WebSocket delta message corruption
  • ✅ Fixes WebSocket final message corruption
  • ✅ Session files remain correct (already trimmed)
  • ✅ Single-point change, minimal impact
  • ✅ Backward compatible (trim doesn't change semantics)

Testing

Unit Tests

All new regression tests pass:

pnpm test src/gateway/server-chat-delta-corruption.test.ts
# ✅ 5 tests passed

Manual Verification

Tested with Site-builder Agent and Java WebSocket client:

  • Before fix: Observed character truncation (xumumxum, GOOGLEGOGLE)
  • After fix: All content correct, no truncation

Impact Assessment

  • Breaking changes: None
  • Performance impact: Negligible (.trim() is O(n) but runs only on stripped text)
  • Compatibility: Fully backward compatible
  • Affected users: Only users consuming WebSocket delta/final with agents using replyToMode: "all"/"threaded"

Closes

Closes #66497 (replace with actual issue number after creating issue)

Evidence

Code Comparison

✅ Correct usage (already using .trim()):

  • src/gateway/session-utils.fs.ts:256
  • src/gateway/session-utils.fs.ts:267
  • extensions/discord/src/voice/sanitize.ts:20

❌ Fixed by this PR:

  • src/gateway/server-chat.ts:691 (cleanedText)
  • src/gateway/server-chat.ts:693 (cleanedDelta)

Session File vs WebSocket Comparison

Session file (correct):

{"content": "...www.6.xumum.xyz...GOOGLE..."}

WebSocket before fix (corrupted):

{"content": "...www.6.xum.xyz...GOGLE..."}

WebSocket after fix (correct):

{"content": "...www.6.xumum.xyz...GOOGLE..."}

Checklist

  • Code changes are minimal and focused
  • Added regression tests
  • All tests pass locally
  • Follows existing code patterns
  • Backward compatible
  • No breaking changes
  • Documentation updated (inline comments)

Related

  • Related to inline directive tag handling in src/utils/directive-tags.ts
  • Aligns with trim patterns in src/gateway/session-utils.fs.ts

Changed files

  • src/gateway/server-chat-delta-corruption.test.ts (added, +147/-0)
  • src/gateway/server-chat.ts (modified, +3/-3)

Code Example

// src/utils/directive-tags.ts:40-65
   const normalized = masked
     .replace(/^\n+/, "")
     .replace(/^[ \t](?=\S)/, "")  // Removes leading space
     .trimEnd();

---

// src/gateway/server-chat.ts:691-693
   const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;  // ❌ No .trim()
   const cleanedDelta = typeof delta === "string" 
     ? stripInlineDirectiveTagsForDisplay(delta).text  // ❌ No .trim()
     : "";

---

LLM output: "[[reply_to_current]] Domain www.6.xumum.xyz"
Pi embedded sends (trimmed): "Domain www.6.xumum.xyz" (no leading space)
Gateway caches (not trimmed): " Domain www.6.xumum.xyz" (has leading space)
Next delta: startsWith() check fails (leading space mismatch)
appendUniqueSuffix: detects false overlap "um" at boundary → truncates
Corruption: "www.6.xum.xyz"
---

"content": "...www.6.xumum.xyz...GOOGLE..."

---

{
  "type": "event",
  "event": "chat",
  "payload": {
    "state": "final",
    "message": {
      "content": [{"type": "text", "text": "...www.6.xum.xyz...GOGLE..."}]
    }
  }
}

---

--- a/src/gateway/server-chat.ts
+++ b/src/gateway/server-chat.ts
@@ -688,9 +688,9 @@ export function createAgentEventHandler({
     text: string,
     delta?: unknown,
   ) => {
-    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;
+    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text.trim();
     const cleanedDelta =
-      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text : "";
+      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text.trim() : "";
     const previousRawText = chatRunState.rawBuffers.get(clientRunId) ?? "";
RAW_BUFFERClick to expand / collapse

GitHub Issue

Title: WebSocket delta corruption with inline directive tags

Labels: bug, gateway, websocket


Bug Description

WebSocket delta and final messages are corrupted when using inline directive tags (e.g., [[reply_to_current]]) at the beginning of LLM output. Characters are truncated due to leading whitespace mismatch between Pi embedded and Gateway processing.

Observed Behavior

Example corruption:

  • Expected: www.6.xumum.xyz

  • Received: www.6.xum.xyz (missing um)

  • Expected: GOOGLE

  • Received: GOGLE (missing O)

Root Cause

Leading whitespace mismatch:

  1. Pi embedded (normalizeDirectiveWhitespace): ✅ Trims leading/trailing whitespace

    // src/utils/directive-tags.ts:40-65
    const normalized = masked
      .replace(/^\n+/, "")
      .replace(/^[ \t](?=\S)/, "")  // Removes leading space
      .trimEnd();
  2. Gateway (stripInlineDirectiveTagsForDisplay in emitChatDelta): ❌ Does NOT trim

    // src/gateway/server-chat.ts:691-693
    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;  // ❌ No .trim()
    const cleanedDelta = typeof delta === "string" 
      ? stripInlineDirectiveTagsForDisplay(delta).text  // ❌ No .trim()
      : "";
  3. Consequence: previousText.startsWith(nextText) fails → appendUniqueSuffix triggers false overlap detection → character truncation

Corruption Mechanism

LLM output: "[[reply_to_current]] Domain www.6.xumum.xyz"
Pi embedded sends (trimmed): "Domain www.6.xumum.xyz" (no leading space)
Gateway caches (not trimmed): " Domain www.6.xumum.xyz" (has leading space)
Next delta: startsWith() check fails (leading space mismatch)
appendUniqueSuffix: detects false overlap "um" at boundary → truncates
Corruption: "www.6.xum.xyz" ❌

Impact

  • ❌ WebSocket delta messages corrupted
  • ❌ WebSocket final messages corrupted (reads from corrupted buffer)
  • ✅ Session files correct (different code path already uses .trim())

Trigger Conditions

  1. Agent config: replyToMode: "all" or "threaded" (default)
  2. LLM outputs inline directive tag at beginning (e.g., [[reply_to_current]], [[audio_as_voice]])
  3. Client consumes WebSocket delta/final messages
  4. Content contains repeated character sequences (triggers false overlap detection)

Evidence

Session file (correct data)

Remote session file on production Gateway contains correct content:

"content": "...www.6.xumum.xyz...GOOGLE..."

Java client WebSocket log (corrupted data)

{
  "type": "event",
  "event": "chat",
  "payload": {
    "state": "final",
    "message": {
      "content": [{"type": "text", "text": "...www.6.xum.xyz...GOGLE..."}]
    }
  }
}

Code comparison

✅ Correct usage (already using .trim()):

  • src/gateway/session-utils.fs.ts:256 - .text.trim()
  • src/gateway/session-utils.fs.ts:267 - .text.trim()
  • extensions/discord/src/voice/sanitize.ts:20 - .text.trim()

❌ Missing .trim() (bug location):

  • src/gateway/server-chat.ts:691 - emitChatDelta cleanedText
  • src/gateway/server-chat.ts:693 - emitChatDelta cleanedDelta

Proposed Fix

Add .trim() to match other code paths:

--- a/src/gateway/server-chat.ts
+++ b/src/gateway/server-chat.ts
@@ -688,9 +688,9 @@ export function createAgentEventHandler({
     text: string,
     delta?: unknown,
   ) => {
-    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text;
+    const cleanedText = stripInlineDirectiveTagsForDisplay(text).text.trim();
     const cleanedDelta =
-      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text : "";
+      typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text.trim() : "";
     const previousRawText = chatRunState.rawBuffers.get(clientRunId) ?? "";

Benefits:

  • ✅ Aligns with session-utils.fs.ts and Discord extension
  • ✅ Fixes delta and final message corruption
  • ✅ Single-point change, minimal impact
  • ✅ Backward compatible (trim doesn't change semantics)

Reproduction Steps

  1. Configure agent with replyToMode: "all"
  2. Send message: "Please respond with: domain www.6.xumum.xyz, search engine GOOGLE"
  3. Monitor WebSocket event: "chat" with state: "final"
  4. Observe character loss: xumumxum, GOOGLEGOGLE

Environment

  • OpenClaw version: Latest main branch (commit 82364e901a as of 2026-04-14)
  • Gateway: Production instance
  • Client: Java WebSocket client monitoring delta/final events
  • Agent: Site-builder with default replyToMode: "all"

Why This Bug Is Rare

  1. Requires LLM to output inline directive tag at beginning
  2. Requires client to consume WebSocket delta/final (most clients read session files, which are correct)
  3. Requires repeated character sequences to trigger false overlap detection
  4. Test coverage gap for inline directive + delta combination

Related Files

  • Bug location: src/gateway/server-chat.ts:691-693
  • Related: src/utils/directive-tags.ts (trim behavior)
  • Correct examples: src/gateway/session-utils.fs.ts:256, extensions/discord/src/voice/sanitize.ts:20

extent analysis

TL;DR

The most likely fix for the WebSocket delta corruption issue is to add a .trim() call when stripping inline directive tags in the emitChatDelta function.

Guidance

  • Verify that the issue is caused by the leading whitespace mismatch between Pi embedded and Gateway processing by checking the normalizeDirectiveWhitespace function in src/utils/directive-tags.ts and the stripInlineDirectiveTagsForDisplay function in src/gateway/server-chat.ts.
  • Apply the proposed fix by adding .trim() to the cleanedText and cleanedDelta variables in src/gateway/server-chat.ts to ensure consistent whitespace handling.
  • Test the fix by following the reproduction steps provided in the issue description and verifying that the character loss is resolved.
  • Review the related files, such as src/gateway/session-utils.fs.ts and extensions/discord/src/voice/sanitize.ts, to ensure that the fix is consistent with other code paths.

Example

The proposed fix can be applied by modifying the emitChatDelta function as follows:

const cleanedText = stripInlineDirectiveTagsForDisplay(text).text.trim();
const cleanedDelta = typeof delta === "string" ? stripInlineDirectiveTagsForDisplay(delta).text.trim() : "";

Notes

The fix is specific to the emitChatDelta function and does not affect other code paths. The issue is rare due to the specific conditions required to trigger the bug, but applying the fix will ensure consistent whitespace handling and prevent character loss.

Recommendation

Apply the workaround by adding .trim() to the cleanedText and cleanedDelta variables in src/gateway/server-chat.ts, as this will fix the leading whitespace mismatch and prevent character loss.

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 - ✅(Solved) Fix WebSocket delta corruption with inline directive tags [1 pull requests, 1 comments, 2 participants]