Skip to content

[Bug]: Discord progress commentary renders a literal leading underscore when an italic line is truncated #88895

@mugabuga

Description

@mugabuga

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

In Discord streaming.mode: "progress" with commentary enabled, some assistant commentary lines in the live progress draft render with a literal leading _ instead of italics, because commentary is underscore-wrapped (_line_) before the shared progress-line truncation runs and that truncation can drop the closing _.

Steps to reproduce

  1. Run OpenClaw 2026.5.31-beta.3 with a Discord guild text channel using streaming.mode: "progress" and progress.commentary: true (full config shape under "Additional provider/model setup details").
  2. Start a turn that emits visible assistant commentary/progress text long enough to exceed the progress-draft line limit (maxLineChars).
  3. Watch the temporary Discord progress draft while it is being edited.

Intermittent: it does not reproduce on every commentary line. It often appears on an early commentary block but, per the screenshots, also affects later commentary blocks.

Expected behavior

A commentary line intended to be italic should render as italic text, or fall back to plain text if truncation would otherwise produce invalid Markdown. It should never display a literal leading _.

Actual behavior

Some commentary lines render with a literal leading underscore and no closing underscore, e.g.:

_I'll turn this into a skill update proposal rather than patching the skill files directly, since reusable skill...

Other commentary blocks in the same progress draft render correctly as italics, so this does not affect all commentary rendering — only lines that hit the truncation path.

OpenClaw version

2026.5.31-beta.3

Operating system

Ubuntu 24.04.4 LTS (kernel 6.17.0-29-generic)

Install method

npm global (openclaw installed under the global node_modules)

Model

openai/gpt-5.5 (effective agents.defaults.model.primary)

Provider / routing chain

Discord guild text channel -> OpenClaw Discord gateway -> OpenAI (Codex/OpenAI OAuth profile) -> openai/gpt-5.5

Additional provider/model setup details

Effective Discord streaming config (observed in the gateway openclaw.json):

{
  "streaming": {
    "mode": "progress",
    "preview": {
      "toolProgress": true,
      "commandText": "raw"
    },
    "progress": {
      "toolProgress": true,
      "commandText": "raw",
      "commentary": true
    }
  }
}

Auth is an OpenAI OAuth profile (Codex/ChatGPT sign-in). No per-channel model override is set for Discord, so the channel uses the default model above.

Logs, screenshots, and evidence

Screenshots showing a literal leading _ in the live Discord progress draft (bot username/app-id redacted) are being added to this issue momentarily:

  • IMG_2461-redacted — first commentary line starts with _I'll look...
  • IMG_2462-redacted — one commentary line starts with _I'll turn..., while the following commentary line renders italicized correctly
  • IMG_2464-redacted — a later (non-first) commentary line starts with _The proposal applied...

Captured literal text from the draft:

_I'll turn this into a skill update proposal rather than patching the skill
files directly, since reusable skill...

Impact and severity

  • Affected: Discord users on streaming.mode: "progress" with progress.commentary: true.
  • Severity: Low. Cosmetic only — it affects the temporary progress draft; final answers are unaffected.
  • Frequency: Intermittent — only commentary lines long enough to be truncated; often an early block but also later blocks.
  • Consequence: The live progress draft shows malformed Markdown (a stray leading _), making in-progress status harder to read.

Additional information

Duplicate search

No existing issue covers this literal-underscore-on-truncation symptom. Closest related, all distinct:

Root cause (source on main @ a46d331, version 2026.5.31-beta.3; verified by source inspection)

Commentary is wrapped in italic underscores before the shared progress-line truncation, and that truncation only rebalances backticks — never the italic _ — so a truncated commentary line keeps an unmatched leading _.

  1. extensions/discord/src/monitor/message-handler.process.ts:1016-1018 routes payload.kind === "preamble" to draftPreview.pushCommentaryProgress(...).

  2. extensions/discord/src/monitor/message-handler.draft-preview.ts, normalizeCommentaryProgressText (lines 473-484) wraps each non-empty line in underscores — balanced at this point:

    return cleaned
      .split(/\r?\n/u)
      .map((line) => line.replace(/\s+/g, " ").trim())
      .filter(Boolean)
      .map((line) => `_${line}_`)   // line 482
      .join("\n");
  3. The draft is rendered via formatChannelProgressDraftText, and core src/channels/streaming.ts:1016 applies compactChannelProgressDraftLine(rawText, maxLineChars).

  4. compactChannelProgressDraftLine (src/channels/streaming.ts:872) truncates the line and finishes every return path through removeUnbalancedInlineBackticks(...) (lines 891 and 914). That helper (lines 855-861) only repairs backticks:

    function removeUnbalancedInlineBackticks(value: string): string {
      const backtickCount = Array.from(value).filter((char) => char === "`").length;
      if (backtickCount % 2 === 0) {
        return value;
      }
      return value.trimStart().startsWith("`") ? value.replaceAll("`", "'") : value.replaceAll("`", "");
    }

So the truncation already knows it must keep inline Markdown balanced — it does exactly that for code spans — but the italic _ (and * bold) wrapper has no equivalent guard. When a commentary line exceeds maxLineChars, truncation drops the trailing _ and leaves the leading one; Discord renders it literally. This matches the observed pattern: only commentary lines long enough to be truncated are affected, which is why it is intermittent and not tied to the first block. Presented as the most likely path given the source, not asserted as the only one.

Suggested fix (mirroring the existing backtick repair; final approach left to maintainers)

Primary — make the compaction boundary underscore-aware, the same way it is already backtick-aware:

  1. Add an underscore counterpart to removeUnbalancedInlineBackticks (or generalize it to a delimiter-aware removeUnbalancedInlineMarker covering `, _, and *) and call it at the same two return sites in compactChannelProgressDraftLine (streaming.ts:891, streaming.ts:914).
  2. To avoid mangling legitimate underscores in arbitrary text (e.g. snake_case, paths), scope the underscore repair to the unmatched leading wrapper only — when the compacted line starts with _ and has an odd _ count, drop just the dangling marker (or fall back to plain text) rather than stripping every _. This is the one place the backtick helper's blunt replaceAll is riskier for _, so the underscore variant should be narrower.

Alternative (more robust, larger change): have the Discord side apply the italic wrap after compaction instead of in normalizeCommentaryProgressText, so the delimiters are added to already-truncated text and can never be split. This keeps core compaction Markdown-agnostic but requires threading "this line is italic commentary" through to post-compaction.

Either way: render commentary as plain text rather than emitting a half-open _ when balancing is not possible.

Tests: there is currently no co-located unit test for these helpers (both are module-private in src/channels/streaming.ts, and there is no message-handler.draft-preview.test.ts under extensions/discord/src/monitor/). Suggest adding a focused case — commentary longer than maxLineChars must not yield a line starting with a lone _ — at whichever seam the fix lands (a new draft-preview test on the Discord side, or a unit test if the repair helper is exported).

Happy to open a PR with the fix plus that test if the approach looks right.

Image Image Image

Filed with AI assistance (Claude Code). Bug behavior is from the reporter's live OpenClaw + Discord install (screenshots attached); the root-cause and fix were verified against openclaw/openclaw main @ a46d331.

Metadata

Metadata

Assignees

Labels

P3Low-priority cleanup, docs, polish, ergonomics, or speculative work.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

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