claude-code - 💡(How to fix) Fix [BUG] TUI crash: MarkupError in ApprovalDialog when content contains "[tag=value:" pattern [1 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
anthropics/claude-code#55583Fetched 2026-05-03 04:49:38
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

ApprovalDialog (Textual) crashes with rich.errors.MarkupError when the content being approved contains text that resembles Rich/Textual markup tags ([name=value] style). The widget appears to render user-supplied content with markup=True instead of escaping [ or passing markup=False.

Error Message

MarkupError: Expected markup value (found '=<token>:\n').

Root Cause

ApprovalDialog (Textual) crashes with rich.errors.MarkupError when the content being approved contains text that resembles Rich/Textual markup tags ([name=value] style). The widget appears to render user-supplied content with markup=True instead of escaping [ or passing markup=False.

Code Example

print(f"[stage] item count = {n}")
print(f"[stage] other count = {m}")

---

MarkupError: Expected markup value (found '=<token>:\n').

---

textual/screen.py:1354 in _refresh_layout
  → textual/_compositor.py:387 in reflow
  → textual/_compositor.py:744 in _arrange_root
  → textual/_compositor.py:601 in add_widget
  → textual/screen.py:543 in arrange
  → textual/_arrange.py:97 in arrange
  → textual/layouts/vertical.py:53 in arrange
  → textual/_resolve.py:254 in resolve_box_models
  → textual/widget.py:1838 in _get_box_model
  → textual/widget.py:1939 in get_content_height
  → textual/widget.py:4471 in _render
  → textual/widgets/_static.py:83 in render
  → textual/widgets/_static.py:62 in visual
    visualize(self, self.__content, markup=self._render_markup)
                                          ^^^^^^^^^^^^^^^^^^^^^

---

Traceback (relevant frames):

textual/screen.py:1354 in _refresh_layout
  → textual/_compositor.py:387 in reflow
  → textual/_compositor.py:744 in _arrange_root
  → textual/_compositor.py:601 in add_widget
  → textual/screen.py:543 in arrange
  → textual/_arrange.py:97 in arrange
  → textual/layouts/vertical.py:53 in arrange
  → textual/_resolve.py:254 in resolve_box_models
  → textual/widget.py:1838 in _get_box_model
  → textual/widget.py:1939 in get_content_height
  → textual/widget.py:4471 in _render
  → textual/widgets/_static.py:83 in render
  → textual/widgets/_static.py:62 in visual
    visualize(self, self.__content, markup=self._render_markup)
                                          ^^^^^^^^^^^^^^^^^^^^^
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Summary

ApprovalDialog (Textual) crashes with rich.errors.MarkupError when the content being approved contains text that resembles Rich/Textual markup tags ([name=value] style). The widget appears to render user-supplied content with markup=True instead of escaping [ or passing markup=False.

Environment

  • Claude Code: 2.1.126
  • OS: Linux 6.17.0-20-generic (Ubuntu 24.04)
  • Python: 3.12.3
  • textual: 8.2.4
  • rich: 15.0.0

Reproduction

Create a file with bracket-prefixed lines that look like markup tags:

print(f"[stage] item count = {n}")
print(f"[stage] other count = {m}")

Trigger any tool call that surfaces this content in the approval dialog (Read / Edit / a Bash command whose preview includes the text). The dialog crashes during layout in _compositor.reflow.

Expected

Dialog renders the content as plain text — [stage], =, etc. .

Actual

MarkupError: Expected markup value (found '=<token>:\n').

Traceback (relevant frames):

textual/screen.py:1354 in _refresh_layout
  → textual/_compositor.py:387 in reflow
  → textual/_compositor.py:744 in _arrange_root
  → textual/_compositor.py:601 in add_widget
  → textual/screen.py:543 in arrange
  → textual/_arrange.py:97 in arrange
  → textual/layouts/vertical.py:53 in arrange
  → textual/_resolve.py:254 in resolve_box_models
  → textual/widget.py:1838 in _get_box_model
  → textual/widget.py:1939 in get_content_height
  → textual/widget.py:4471 in _render
  → textual/widgets/_static.py:83 in render
  → textual/widgets/_static.py:62 in visual
    visualize(self, self.__content, markup=self._render_markup)
                                          ^^^^^^^^^^^^^^^^^^^^^

The crashing widget is Static() inside ApprovalDialog → Vertial-from-member, PromptOptionsTable, Static#approval-auto-hint,Static]. _render_markup is True, so any [ in user content is treated as markup.

Suspected fix

In the Static(...) constructions inside ApprovalDialog (and der user-provided text — tool result preview, prompt body, etc.),either:

  1. Pass markup=False, or
  2. Pre-escape with rich.markup.escape(text) before passing to

Either approach renders [tag=value] content literally and avoi

Frequency

Reliably reproducible. Occurs whenever the approval dialog body contains [tag=value]-shaped substrings — common in:

  • Python print statements with bracketed log prefixes
  • Markdown notes quoting such code
  • Any CLI output reproduced as a code block

In affected projects the crash recurs every few tool calls untile.

What Should Happen?

Expected

Dialog renders the content as plain text — [stage], =, etc. should be displayed verbatim.

Error Messages/Logs

Traceback (relevant frames):

textual/screen.py:1354 in _refresh_layout
  → textual/_compositor.py:387 in reflow
  → textual/_compositor.py:744 in _arrange_root
  → textual/_compositor.py:601 in add_widget
  → textual/screen.py:543 in arrange
  → textual/_arrange.py:97 in arrange
  → textual/layouts/vertical.py:53 in arrange
  → textual/_resolve.py:254 in resolve_box_models
  → textual/widget.py:1838 in _get_box_model
  → textual/widget.py:1939 in get_content_height
  → textual/widget.py:4471 in _render
  → textual/widgets/_static.py:83 in render
  → textual/widgets/_static.py:62 in visual
    visualize(self, self.__content, markup=self._render_markup)
                                          ^^^^^^^^^^^^^^^^^^^^^

Steps to Reproduce

Suspected fix

In the Static(...) constructions inside ApprovalDialog (and any sibling widgets that render user-provided text — tool result preview, prompt body, etc.), either:

  1. Pass markup=False, or
  2. Pre-escape with rich.markup.escape(text) before passing to Static.

Either approach renders [tag=value] content literally and avoids the parser path entirely.

Frequency

Reliably reproducible. Occurs whenever the approval dialog body contains [tag=value]-shaped substrings — common in:

  • Python print statements with bracketed log prefixes
  • Markdown notes quoting such code
  • Any CLI output reproduced as a code block

In affected projects the crash recurs every few tool calls until those files are out of scope.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.1.126

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

No response

extent analysis

TL;DR

Passing markup=False or pre-escaping user-provided text with rich.markup.escape(text) in Static widget constructions inside ApprovalDialog is likely to fix the crash.

Guidance

  • Identify all instances of Static widgets within ApprovalDialog that render user-provided text and modify them to either pass markup=False or pre-escape the text.
  • Verify that the crash no longer occurs when the approval dialog body contains [tag=value]-shaped substrings.
  • Consider adding input validation or sanitization to prevent similar issues in the future.
  • Review the ApprovalDialog code to ensure that all user-provided text is properly escaped or rendered as plain text.

Example

from rich.markup import escape

# Before
Static("User-provided text with [tag=value]")

# After (using markup=False)
Static("User-provided text with [tag=value]", markup=False)

# After (using rich.markup.escape)
Static(escape("User-provided text with [tag=value]"))

Notes

The provided fix assumes that the issue is caused by the Static widget treating user-provided text as markup. If the issue persists after applying the suggested fix, further investigation may be necessary.

Recommendation

Apply the workaround by passing markup=False or pre-escaping user-provided text with rich.markup.escape(text) in Static widget constructions inside ApprovalDialog, as this is a reliable and straightforward solution to the identified issue.

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

claude-code - 💡(How to fix) Fix [BUG] TUI crash: MarkupError in ApprovalDialog when content contains "[tag=value:" pattern [1 participants]