Known issues and limitations¶
What does not work yet, what was never meant to, and what works differently on purpose. Three different things, kept apart — a deliberate trade-off listed as a bug makes a considered decision look like neglect.
Applies to the 0.0.24 release (rs-rich-cli 0.0.24, rs-rich 0.0.14,
published 2026-11-03), reviewed against Python rich 15.0.0. Older entries
name the release they were found in. Each entry links to its issue or report
so you can check the status without waiting for this page to be updated.
Bugs¶
Things that should work and do not.
Narrow --json output is display output, not machine-readable JSON¶
Scope. Default builds match upstream: narrow output may wrap or crop inside
an escape, and --width crops overlong JSON lines. The optional, off-by-default
json-escape-safe Cargo feature avoids partial escapes when cropping and keeps
escapes together when folding at widths that can fit them. See
DIVERGENCES §22.
Neither mode promises machine-readable output; use the original JSON with jq
when every value must survive.
The recorder keeps fewer frames on macOS¶
rich record keeps every screen update the program finished, up to the
tape's frame rate. On Linux a game recorded at Set Framerate 35 keeps
about 30 frames a second; on CI's macOS runners the same recording kept 7
frames over 0.8 s, so its video plays in jumps. 0.0.22 splits a late read
into the updates inside it, which did not fix macOS: its runners still kept
5 frames over 0.5 s. The cause there is not known yet, and the frame-rate
test keeps its lower bound on Linux only until it is. Record games on Linux
for now. Found in 0.0.20, still open in 0.0.24
(plan).
An ordered Markdown item holding only an empty link loses its row under justify="full"¶
The differential fuzzer found it in 0.0.22: an ordered list item whose
only content is an empty link, 2) [](), renders no row when the
Markdown is fully justified, where rich 15.0.0 prints the item's number.
With the default justify the output matches. The case is kept in
scripts/fixtures/diff_rich_known.jsonl until it is fixed
(#769).
Limitations¶
Things that are not implemented, and are not claimed to be.
Windows legacy console is not supported¶
rich assumes a VT-capable terminal. On Windows it enables virtual terminal
processing; on a console that cannot do VT (very old cmd.exe), output will
contain raw escape sequences.
Upstream falls back to the Win32 console API for these. The port will not:
every supported Windows release has a VT-capable console, so this is a
permanent divergence (DIVERGENCES §38,
#12).
Workaround: use Windows Terminal, or --no-color.
The local time zone on Windows follows TZ when it is set¶
rs-rich-interact's Zone::Local (and so DateTime::now_local, natural
dates and intuiTUIve's calendars) asks Windows for the system's zone,
unless TZ is set: then TZ wins, as it does for the C runtime. Windows
has no zoneinfo database, so TZ must be a POSIX rule
(CET-1CEST,M3.5.0,M10.5.0/3) or TZDIR must name a zoneinfo directory;
a zone name alone, such as Europe/Berlin, is read as UTC. See
Troubleshooting.
Chrome's DevTools port on Windows in rs-rich-embed¶
On Windows, the Chrome engine of rs-rich-embed's web view talks to
Chrome over DevTools on a random port of 127.0.0.1, which other programs
and users on the machine can reach while the view is open, and DevTools
has no authentication. On Windows, use the Chrome engine on a machine you
do not share. On Linux and macOS it uses a pipe instead and opens no port
(since rs-rich-embed 0.0.4, 0.0.21).
Windows keeps the port until a maintained crate can pass Chrome the pipe's
two inheritable handles in one switch
(why).
No Jupyter integration, and no Python-object inspection¶
jupyter.py and inspect/repr have no equivalent: they render live Python
objects, which has no meaning in a Rust port. pretty and traceback are
reimplemented Rust-natively instead — see
Divergences.
SVG export is not self-contained offline¶
--export-svg references its font from a CDN. The HTML from --export-html
is self-contained. If you need an offline SVG, embed the font yourself after
export.
GIF interruption and export limitations¶
--gif-mode is ascii (the default, and the redirected/colorless fallback) or
blocks; see the capability matrix.
Since 0.0.11, --image-color and --image-dither also apply to GIF frames.
Normal playback restores the cursor; Ctrl-C can leave it hidden. GIF export and
Sixel playback are unsupported.
Redaction is experimental and best effort¶
rich_ext::redact, rich capture --redact and --redact-pattern, and
rich inspect --redact mask what their detectors recognise: secret-named keys,
bearer tokens, common token prefixes, AWS key ids, JWTs and URL passwords. They
can miss a secret in a format they do not know. Read the output, and any SVG,
HTML or cast file written from it, before sharing it, and
report
anything that gets through. rich env masking is best effort in the same way.
Viewers read a bounded amount¶
view, hex, unicode, inspect, capture and --theme-file stop at a
fixed size (for example 8 MiB or 20,000 lines for source in view, 64 MiB per
inspect document, 1 MiB of capture output), so an endless input ends instead
of exhausting memory. Larger inputs are shown in part or refused; the caps are
not configurable. See Limits.
Mermaid subgraphs are drawn without frames; DOT's limits¶
rs-rich-diagram 0.0.2 frames DOT cluster… subgraphs and honours
rank=same where a layered drawing can (a group an edge runs within is
dropped, with a note); other rank values are accepted with a note. Mermaid
flowcharts do not pass their subgraphs on as clusters yet, so those are
still drawn without frames, with a note. ER diagrams attach relationship
edges to an entity's box, not to the column's row. Node ports, HTML-like
labels, record shapes and several graphs in one file are refused with the
construct and its line (rich dot exits 4). For Graphviz's full layout, use
--dot-backend graphviz --export-svg with Graphviz installed. See
Diagrams.
SVG export can squeeze CJK glyphs¶
SVG textLength uses character counts for some runs; wide CJK glyphs may overlap in the exported image. The same case reproduces in pinned Python Rich 15.0.0. Decoded text and plain terminal output retain the original characters. This is an export-layout limitation, separate from encoding support.
CSV output still retains source rows¶
Undecorated CSV output streams styled rows, reducing output-buffer overhead. 0.0.4 removes duplicate parsed-cell storage and trims row capacity, reducing the measured 100k-row peak RSS by about 46%. Source rows remain in memory for measurement, so memory still scales with input. Decorated, aligned, paged and exported CSV output still buffers. The 0.0.4 work in #74 reduces retained memory; it does not provide bounded-memory processing.
Long-line rendering still allocates memory¶
0.0.4 removes repeated UTF-8 prefix scans during Text wrapping. On the recorded Linux benchmark, the 5 MiB Markdown paragraph now finishes in 531 ms; it previously timed out after 10 seconds. Source and rendered lines still occupy memory. See measured results for input-path distinctions, output verification and reproducible samples.
Headerless text encodings must be selected explicitly¶
0.0.4 supports --encoding utf-16, utf-16le, utf-16be and
utf-8. Default files retain UTF-8 replacement decoding; stdin and URLs remain
strict UTF-8. A recognized UTF-16 BOM gets an actionable hint. Headerless UTF-16
is not guessed: select its byte order explicitly or convert to UTF-8. See
encoding troubleshooting.
Syntax parsing remains costly for varied source¶
The off-by-default 0.0.4 syntax-cache Cargo feature helps repeated boilerplate
whose parser state stays unchanged. The off-by-default onig feature (0.0.11)
makes all highlighting 2–4× faster by using Oniguruma, at the cost of a C build
dependency. Default builds use uncached Syntect. Measured real-source files showed essentially unchanged runtime;
loading and parsing new syntax still costs more than plain text. See the
workload-specific results.
Historical 0.0.2 Windows/Python comparisons are retained separately in the
benchmark page; they are not current 0.0.4 measurements. The implemented scope
of #45 is repeated-line
parsing reuse, not a blanket syntax speedup.
Deliberate divergences¶
Things that work differently on purpose. The full list, each with its reasoning, is in Divergences; these are the ones you are most likely to notice.
Highlighted code does not match Python byte-for-byte¶
Upstream uses Pygments; this port uses syntect, which ships different grammars
and themes. Colours therefore differ. Everything around the code — width,
padding, wrapping, the background block — is parity-tested.
Replacing syntect would mean shipping a Pygments-equivalent lexer set, which is
out of scope. This is divergence #18.
Raw ESC in input reaches the terminal in the upstream modes¶
In the modes ported from upstream (rich FILE, --print, --markdown,
--json, …), an escape character in a rendered file is passed through, exactly
as upstream passes it through. It is listed here because it surprises people,
not because it is a defect: matching upstream is the project's whole purpose.
BEL, backspace, vertical tab and form feed are stripped, as upstream
strips them.
For untrusted input, pass --sanitize (since 0.0.5,
#64), which shows
terminal controls as inert symbols. rich view and text rich diff, which have
no upstream behaviour to keep, sanitise by default; --no-sanitize turns that
off. See Neutralize terminal controls.
Hyperlinks have no random id=¶
Upstream tags OSC 8 hyperlinks with a random id derived from Python's RNG. Reproducing the byte sequence would mean reproducing the RNG, so ours omits it. Terminals do not depend on it. Divergence #20.
Fixed recently¶
Kept here so anyone on an older build still finds the symptom. Problems found
and fixed in features that were never published (such as the 0.0.11 release-test
fixes to view, stack traces and hyperlinks) are in the changelog instead. Full detail in
the changelog.
Reporting something not listed here¶
Please open an issue
with the exact command, the input if you can share it, and what you expected. If
it is a parity difference from Python rich, Reporting a parity
bug explains what makes those reports useful.