openclaw - ✅(Solved) Fix Fenced code block indentation lost when response contains 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#50010Fetched 2026-04-08 01:00:21
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1

Root Cause

normalizeDirectiveWhitespace() in src/utils/directive-tags.ts applies whitespace normalization to the entire text including fenced code block content:

function normalizeDirectiveWhitespace(text: string): string {
  return text
    .replace(/[ \t]+/g, " ")           // collapses all space runs
    .replace(/[ \t]*\n[ \t]*/g, "\n")  // strips spaces around newlines
    .trim();
}

This function is called by parseInlineDirectives() on every response that contains [[ directive tags (and even on the early-return path without directives).

Fix Action

Fix

#50009

PR fix notes

PR #50009: fix(directives): preserve fenced code block indentation

Description (problem / solution / changelog)

Summary

normalizeDirectiveWhitespace() in src/utils/directive-tags.ts unconditionally collapses all runs of spaces/tabs and strips whitespace around newlines. This destroys indentation inside fenced code blocks in every LLM response that contains inline directive tags like [[reply_to_current]].

Fixes #50010

Before (bug): JSON inside ```json ``` loses all indentation — every line starts at column 0.

After (fix): Indentation inside fenced code blocks is preserved; whitespace normalization only applies to text outside fences.

Root cause

// Before — normalizes EVERYTHING including code blocks
function normalizeDirectiveWhitespace(text: string): string {
  return text
    .replace(/[ \t]+/g, " ")           // collapses "  key" → " key"
    .replace(/[ \t]*\n[ \t]*/g, "\n")  // strips "  " after newlines
    .trim();
}

Fix

Reuses the existing parseFenceSpans() from src/markdown/fences.ts (no new dependencies) to identify fenced code block regions and skip normalization inside them.

Evidence

  • Confirmed via session log: LLM raw output has proper 2/4/6-space indentation inside a fenced JSON code block, but the delivered Telegram message shows zero indentation.
  • markdownToTelegramHtml() preserves indentation correctly (verified by test) — the stripping happens in normalizeDirectiveWhitespace() before the text reaches the rendering pipeline.

Test plan

  • Existing directive-tags.test.ts tests pass (6/6)
  • New tests: fenced code block indentation preserved with and without directives (3/3)
  • Integration test: parseInlineDirectives -> markdownToTelegramHtml produces <pre><code> with correct indentation
  • format.test.ts tests pass (16/16)

Generated with Claude Code

Changed files

  • src/utils/directive-tags.test.ts (modified, +25/-0)
  • src/utils/directive-tags.ts (modified, +22/-4)

Code Example

[[reply_to_current]] Here is the config:

---



---

{
   "preferences": {
   "enabled": true
   }
   }

---

function normalizeDirectiveWhitespace(text: string): string {
  return text
    .replace(/[ \t]+/g, " ")           // collapses all space runs
    .replace(/[ \t]*\n[ \t]*/g, "\n")  // strips spaces around newlines
    .trim();
}
RAW_BUFFERClick to expand / collapse

Bug description

When an LLM response contains inline directive tags (e.g. [[reply_to_current]]), all indentation inside fenced code blocks is stripped before the message reaches the rendering pipeline.

Steps to reproduce

  1. Have an LLM respond with a [[reply_to_current]] tag and a fenced JSON code block:

    [[reply_to_current]] Here is the config:
    
    ```json
    {
      "preferences": {
        "enabled": true
      }
    }
    ```
  2. The delivered message shows the JSON with zero indentation:

    {
    "preferences": {
    "enabled": true
    }
    }

Root cause

normalizeDirectiveWhitespace() in src/utils/directive-tags.ts applies whitespace normalization to the entire text including fenced code block content:

function normalizeDirectiveWhitespace(text: string): string {
  return text
    .replace(/[ \t]+/g, " ")           // collapses all space runs
    .replace(/[ \t]*\n[ \t]*/g, "\n")  // strips spaces around newlines
    .trim();
}

This function is called by parseInlineDirectives() on every response that contains [[ directive tags (and even on the early-return path without directives).

Expected behavior

Indentation inside fenced code blocks should be preserved. Only whitespace outside code blocks should be normalized after stripping directive tags.

Fix

#50009

extent analysis

Fix Plan

To preserve indentation inside fenced code blocks, we need to modify the normalizeDirectiveWhitespace() function to exclude code blocks from whitespace normalization.

Here are the steps:

  • Identify fenced code blocks in the text using a regular expression.
  • Temporarily remove these code blocks from the text.
  • Apply whitespace normalization to the remaining text.
  • Reinsert the code blocks at their original positions.

Example Code

function normalizeDirectiveWhitespace(text: string): string {
  // Regular expression to match fenced code blocks
  const codeBlockRegex = /```[^```]*```/gs;

  // Find all code blocks and store them in an array
  const codeBlocks = text.match(codeBlockRegex) || [];

  // Replace code blocks with placeholders
  const placeholders = {};
  codeBlocks.forEach((block, index) => {
    const placeholder = `{{codeBlock_${index}}}`;
    placeholders[placeholder] = block;
    text = text.replace(block, placeholder);
  });

  // Apply whitespace normalization
  text = text
    .replace(/[ \t]+/g, " ")
    .replace(/[ \t]*\n[ \t]*/g, "\n")
    .trim();

  // Reinsert code blocks
  Object.keys(placeholders).forEach((placeholder) => {
    text = text.replace(placeholder, placeholders[placeholder]);
  });

  return text;
}

Verification

To verify that the fix worked, you can test the normalizeDirectiveWhitespace() function with a sample text containing a fenced code block and a directive tag. The indentation inside the code block should be preserved after normalization.

Extra Tips

  • Make sure to update the unit tests for the normalizeDirectiveWhitespace() function to cover the new behavior.
  • Consider adding a flag to the function to enable or disable code block preservation, in case this behavior needs to be toggled in the future.

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

Indentation inside fenced code blocks should be preserved. Only whitespace outside code blocks should be normalized after stripping directive tags.

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 Fenced code block indentation lost when response contains inline directive tags [1 pull requests, 1 comments, 2 participants]