openclaw - 💡(How to fix) Fix Reply context body truncated at 500 chars hides actionable content from bot's prior message [1 pull requests]

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…

When a user replies to a bot message, the prior message is injected into the next turn as [reply target] content. That body is sanitized by sanitizeTranscriptField, which caps at MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500 in src/auto-reply/reply/inbound-meta.ts. The same 500-char cap also applies to short fields (sender, message_id, media_type, media_ref).

For multi-paragraph bot replies (analyses, option lists, "should I do X?" prompts) the body gets sliced mid-sentence, often before the actionable content (e.g. proposed options). The next turn sees only the preamble, can't tell what the user's short reply refers to, and either asks for context back or hallucinates an unrelated identity from the surviving prefix.

This compounds with idle session resets: if the original session was reset between turns, the reply context is the only surviving signal — and 500 chars isn't enough.

Root Cause

src/auto-reply/reply/inbound-meta.ts:

const MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500;

function sanitizeTranscriptField(value) { /* used for body AND short fields */ }

// in formatChatWindowMessage:
const body = sanitizeTranscriptField(value["body"]);  // long-form content, capped at 500

The same function and constant handle long-form body and short metadata fields. 500 is fine for sender / message_id; too aggressive for body.

No config knob exposes this — openclaw.json can't override it.

Fix Action

Fixed

Code Example

const MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500;

function sanitizeTranscriptField(value) { /* used for body AND short fields */ }

// in formatChatWindowMessage:
const body = sanitizeTranscriptField(value["body"]);  // long-form content, capped at 500

---

const MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500;
+const MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS = 8000;

 function sanitizeTranscriptField(value) { /* unchanged */ }
+function sanitizeTranscriptBody(value) {
+  const body = sanitizePromptBody(value);
+  if (!body) return;
+  const truncated = body.length <= MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS
+    ? body
+    : `${truncateUtf16Safe(body, MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS - 14).trimEnd()}…[truncated]`;
+  return neutralizeMarkdownFences(truncated).replace(/\s+/g, " ").trim();
+}

 // in formatChatWindowMessage:
-const body = sanitizeTranscriptField(value["body"]);
+const body = sanitizeTranscriptBody(value["body"]);
RAW_BUFFERClick to expand / collapse

Summary

When a user replies to a bot message, the prior message is injected into the next turn as [reply target] content. That body is sanitized by sanitizeTranscriptField, which caps at MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500 in src/auto-reply/reply/inbound-meta.ts. The same 500-char cap also applies to short fields (sender, message_id, media_type, media_ref).

For multi-paragraph bot replies (analyses, option lists, "should I do X?" prompts) the body gets sliced mid-sentence, often before the actionable content (e.g. proposed options). The next turn sees only the preamble, can't tell what the user's short reply refers to, and either asks for context back or hallucinates an unrelated identity from the surviving prefix.

This compounds with idle session resets: if the original session was reset between turns, the reply context is the only surviving signal — and 500 chars isn't enough.

Repro

  1. Have a bot produce a multi-paragraph message (>500 chars), e.g. an analysis ending with "Option A / B / C — which one?"
  2. Wait long enough for the session to idle-reset (default session.reset.idleMinutes=15), or otherwise force a new session.
  3. User sends a short reply like "A".
  4. Inspect the new session's first user message JSONL — the injected Conversation context body ends at …[truncated] somewhere before the options.
  5. Bot responds with "what is option A?" / disclaims role / asks for context.

Real-world evidence (own deployment, 2026-05-27):

  • Original bot message: ~1.8 KB analysis with options A/B/C at the end
  • Injected [reply target] body in new session: truncated at ~486 chars, options never reached
  • Bot in new session: claimed to be a different agent role, asked user to re-paste context

Root cause

src/auto-reply/reply/inbound-meta.ts:

const MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500;

function sanitizeTranscriptField(value) { /* used for body AND short fields */ }

// in formatChatWindowMessage:
const body = sanitizeTranscriptField(value["body"]);  // long-form content, capped at 500

The same function and constant handle long-form body and short metadata fields. 500 is fine for sender / message_id; too aggressive for body.

No config knob exposes this — openclaw.json can't override it.

Proposed fix

Split body cap from generic field cap. Minimal diff:

 const MAX_UNTRUSTED_TRANSCRIPT_FIELD_CHARS = 500;
+const MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS = 8000;

 function sanitizeTranscriptField(value) { /* unchanged */ }
+function sanitizeTranscriptBody(value) {
+  const body = sanitizePromptBody(value);
+  if (!body) return;
+  const truncated = body.length <= MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS
+    ? body
+    : `${truncateUtf16Safe(body, MAX_UNTRUSTED_TRANSCRIPT_BODY_CHARS - 14).trimEnd()}…[truncated]`;
+  return neutralizeMarkdownFences(truncated).replace(/\s+/g, " ").trim();
+}

 // in formatChatWindowMessage:
-const body = sanitizeTranscriptField(value["body"]);
+const body = sanitizeTranscriptBody(value["body"]);

8000 is a starting suggestion — generous enough for multi-paragraph reasoning + a mid-sized table, still a hard cap against untrusted-content prompt-budget abuse. Could also be made configurable via openclaw.json if you prefer.

Impact

  • Affects every channel that uses the inbound-meta reply context (Telegram, Slack, Discord, etc. via the shared formatChatWindowMessage path).
  • Hits hardest when (a) bots produce long-form replies and (b) sessions reset between turns — i.e. exactly the multi-turn reasoning workflows openclaw is built for.
  • Silent failure: from the user's perspective the bot just "got confused" or "forgot what it said". Easy to misdiagnose as a model issue.

Happy to send a PR if direction is acceptable.

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 Reply context body truncated at 500 chars hides actionable content from bot's prior message [1 pull requests]