Keep literal reflex.Var tag text from hanging or corrupting string vars
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- Bug fix (non-breaking change which fixes an issue)
Description
A string prop containing literal <reflex.Var>...</reflex.Var> text — for example an app that documents Reflex itself, or pasted console output — is treated as an encoded global var regardless of what the tags hold. LiteralStringVar.create / Var.__post_init__ then fail in three ways (all reproduced on main @ aeea15a):
| Tag content | Failure |
|---|---|
non-numeric (<reflex.Var>state</reflex.Var>) | create spins forever: the while loop never advances value on a non-numeric match, so search finds the same match again (stack dump pinned at the while, 15 s faulthandler timeout) |
numeric but unregistered (<reflex.Var>1234567890</reflex.Var>) | KeyError from _global_vars[int(serialized_data)] |
empty (<reflex.Var></reflex.Var>) | IndexError from serialized_data[0] |
The decode path (_decode_var_immutable, called from every Var.__post_init__) crashes identically on empty/unknown tags — and it strips non-var tag text from the compiled _js_expr, silently dropping the user's words from the rendered output.
Fix
Treat tag content as an encoded var only when its hash actually resolves in _global_vars; anything else is the user's own text:
LiteralStringVar.create: on a non-var match, keep the matched text verbatim and continue scanning after the closing tag, so the loop always advances. Literal-only results are now constructed directly instead of being re-parsed throughLiteralVar.create, which would have recursed infinitely on preserved text._decode_var_immutable: same registry guard, scans by position, and leaves non-var tag text in the expression instead of stripping or crashing on it.
Real encoded markers behave exactly as before — the registry hit short-circuits to the existing code path.
Tests
New tests/units/reflex_base/vars/test_sequence.py (5 tests). On main: test_marker_like_text_is_preserved_verbatim and test_real_var_and_marker_like_text_interpolate_together hang (killed by an external 60 s timeout, faulthandler dump at sequence.py:1528/:1533), and test_unknown_marker_hash_is_preserved_verbatim / test_empty_marker_text_is_preserved_verbatim / test_var_data_decode_tolerates_marker_like_text raise KeyError / IndexError. With this PR all 5 pass.
uv run pytest tests/units/reflex_base/vars— 193 passeduv run pytest tests/units/test_var.py— 252 passed- Full
tests/units: 14246 passed, 11 failed — all 11 are pre-existing Windows symlink/WinErrorfailures; running the same files on cleanmain(stashed) fails identically, so they are unrelated to this change. ruff check/ruff format --checkclean;pyrightclean on the changed modules.
Validation
- Value and compiled JS both keep the literal text:
LiteralStringVar.create('use <reflex.Var>state</reflex.Var> here')renders"use <reflex.Var>state</reflex.Var> here"unchanged. - Mixed f-strings still interpolate:
f"{var} wrote <reflex.Var>fear</reflex.Var> down"decodes the real var (state hooks intact) and keeps the tag text as literal content.
Changes To Core Features:
- Have you added an explanation of what your changes do and why you'd like us to include them?
- Have you written new tests for your core changes, as applicable?
- Have you successfully ran tests with your changes locally?
After these steps, you're ready to open a pull request.
a. Descriptive title above.
b. Description above.
c. No linked issue — self-found via code inspection.