Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/+stateful-pages-marker-decode.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A `stateful_pages.json` marker that is not valid UTF-8 is now treated as corrupt and rebuilt, instead of raising `UnicodeDecodeError` on every backend start.
8 changes: 5 additions & 3 deletions reflex/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1210,16 +1210,18 @@ def _register_plugin_routes(app: App, plugins: Sequence[Plugin]) -> None:
def _read_stateful_pages_marker() -> list[str] | None:
"""Read the routes that create state classes from a previous compile.

A missing marker or one truncated by an older writer requires full page
A missing marker or one corrupted by an older writer requires full page
evaluation. New writers replace the marker atomically.

Returns:
The stateful routes, or None if no valid marker has been written yet.
"""
marker = prerequisites.get_backend_dir() / constants.Dirs.STATEFUL_PAGES
try:
return json.loads(marker.read_text())
except (FileNotFoundError, json.JSONDecodeError):
return json.loads(marker.read_bytes())
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
except (FileNotFoundError, ValueError):
# ValueError covers both JSONDecodeError and the UnicodeDecodeError of a
# marker that is not valid UTF-8.
return None
except PermissionError:
if constants.IS_WINDOWS:
Expand Down
17 changes: 17 additions & 0 deletions tests/units/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5498,6 +5498,23 @@ def test_compile_dry_run_preserves_stateful_marker(compilable_app, mocker, exist
assert marker.read_text() == '["previous"]'


@pytest.mark.parametrize(
"content",
[
pytest.param(b'["index"', id="truncated"),
pytest.param(b"\xff\xfe garbage", id="not-utf-8"),
Comment thread
JohannesGezachew marked this conversation as resolved.
Outdated
],
)
def test_read_stateful_pages_marker_rejects_corrupt_marker(
tmp_path: Path, mocker: MockerFixture, content: bytes
):
"""A corrupt marker asks for a full page evaluation instead of raising."""
mocker.patch("reflex.utils.prerequisites.get_backend_dir", return_value=tmp_path)
(tmp_path / constants.Dirs.STATEFUL_PAGES).write_bytes(content)

assert _read_stateful_pages_marker() is None


def test_write_stateful_pages_marker_concurrent_readers_see_valid_json(
tmp_path: Path, mocker: MockerFixture
):
Expand Down
Loading