Repository navigation
Conversation
A backend var used as a component child raised a ChildrenTypeError that named neither the var nor a fix, and as a typed prop or an event handler argument a generic error that printed its default value. Comparing it (or a mixin's field) with a value or truth-testing it gave a plain False or True, so rx.cond on it always took one branch, and on_load accepted it. These now raise BackendVarFormatError. Fields still compare and hash by identity. Closes reflex-dev#7843.
[Medium impact] The PR changes how backend-only fields behave across shared UI and page setup paths. The full-message assertions required by the repository must be added before merging. Findings
SummaryThis PR makes more backend-var UI mistakes raise
The new error-message tests must check the full message to meet the repository's testing rule. Reviews (1) · Last reviewed commit: "fix: raise BackendVarFormatError for the..." · Reviewed by Greptile |
| with pytest.raises( | ||
| BackendVarFormatError, | ||
| match=r"Backend var 'BackendPropState\._size' exists only", | ||
| ) as exc_info: | ||
| create(BackendPropState._size) | ||
| assert "default=" not in str(exc_info.value) |
There was a problem hiding this comment.
Error checks miss message changes. These new assertions match only the start of the error, so they still pass if the suggested fix disappears or becomes wrong. The repository requires tests to compare complete error messages and explicitly forbids partial regex matches. Compare the full expected message here and in the other new checks, including those in
test_base.py and test_app.py.
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
5 issues found across 12 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/units/test_app.py">
<violation number="1" location="tests/units/test_app.py:352">
P3: The new `pytest.raises` calls match only a fragment of the error message via a partial regex. Per `AGENTS.md`, assert the full error message exactly, so a changed or truncated message is caught. Please match the complete expected text in both new tests.</violation>
</file>
<file name="tests/units/test_event.py">
<violation number="1" location="tests/units/test_event.py:278">
P3: This `match` is a partial regex, so the test passes for any message starting with this prefix. AGENTS.md asks for the full error message to be compared exactly. Match the complete `BackendVarFormatError` text from `_no_frontend_var_error`, with the owner and field names fixed.</violation>
</file>
<file name="docs/changelog/upgrading/upgrading-to-0-11.md">
<violation number="1" location="docs/changelog/upgrading/upgrading-to-0-11.md:244">
P3: Logging does not raise here. With %-style arguments, the stdlib logging handler catches the error and prints a `--- Logging error ---` report. Name `print()` and `str()` as the raising cases, and say that `repr()` is the way to log the field.</violation>
</file>
<file name="docs/changelog/upgrading/upgrading-to-0-10.md">
<violation number="1" location="docs/changelog/upgrading/upgrading-to-0-10.md:12">
P2: This paragraph documents the `str()`/f-string raising as 0.10 behavior, but the 0.11 guide and the v0.11.0 changelog put it in 0.11, and v0.10.1 does not mention it. Move the `str()`/f-string sentence to the 0.11 guide only, or correct the version it names.</violation>
</file>
<file name="tests/units/components/test_component.py">
<violation number="1" location="tests/units/components/test_component.py:799">
P3: This prefix-only `match` passes even if the rest of the error, including its suggested fix, changes. Assert the complete expected message here and in the other new backend-var error checks.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| ## Reading a backend var on a state class | ||
|
|
||
| Reading a backend var on the state class, such as `State._items`, now returns its `Field` instead of the var's default value ([#7312](https://github.com/reflex-dev/reflex/pull/7312)). Passing it to a component raises `ChildrenTypeError` (as a child); a prop may raise `BackendVarFormatError` when its value is built or rendered (earlier 0.10 releases may instead raise `TypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar`), while typed props can still reject it with `TypeError` during component creation. Turning it into text with an f-string, `str.format()`, `str()` or `%` raises `BackendVarFormatError` (earlier 0.10 releases embed the `Field` text for `!s`, `str()` and `%`; `!r` and `repr()` still do). Reading the var on a state instance, such as `self._items`, is unchanged. | ||
| Reading a backend var on the state class, such as `State._items`, now returns its `Field` instead of the var's default value ([#7312](https://github.com/reflex-dev/reflex/pull/7312)). Passing it to a component as a child or a prop raises `BackendVarFormatError` (earlier releases may instead raise `ChildrenTypeError` for a child, `TypeError` for a typed prop, or `TypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar` when the value is built or rendered). Turning it into text with an f-string, `str.format()`, `str()` or `%` raises `BackendVarFormatError` (earlier 0.10 releases embed the `Field` text for `!s`, `str()` and `%`; `!r` and `repr()` still do). Reading the var on a state instance, such as `self._items`, is unchanged. |
There was a problem hiding this comment.
P2: This paragraph documents the str()/f-string raising as 0.10 behavior, but the 0.11 guide and the v0.11.0 changelog put it in 0.11, and v0.10.1 does not mention it. Move the str()/f-string sentence to the 0.11 guide only, or correct the version it names.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/changelog/upgrading/upgrading-to-0-10.md, line 12:
<comment>This paragraph documents the `str()`/f-string raising as 0.10 behavior, but the 0.11 guide and the v0.11.0 changelog put it in 0.11, and v0.10.1 does not mention it. Move the `str()`/f-string sentence to the 0.11 guide only, or correct the version it names.</comment>
<file context>
@@ -9,7 +9,7 @@ This page covers the 0.10 changes most likely to break an existing app, or to be
## Reading a backend var on a state class
-Reading a backend var on the state class, such as `State._items`, now returns its `Field` instead of the var's default value ([#7312](https://github.com/reflex-dev/reflex/pull/7312)). Passing it to a component raises `ChildrenTypeError` (as a child); a prop may raise `BackendVarFormatError` when its value is built or rendered (earlier 0.10 releases may instead raise `TypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar`), while typed props can still reject it with `TypeError` during component creation. Turning it into text with an f-string, `str.format()`, `str()` or `%` raises `BackendVarFormatError` (earlier 0.10 releases embed the `Field` text for `!s`, `str()` and `%`; `!r` and `repr()` still do). Reading the var on a state instance, such as `self._items`, is unchanged.
+Reading a backend var on the state class, such as `State._items`, now returns its `Field` instead of the var's default value ([#7312](https://github.com/reflex-dev/reflex/pull/7312)). Passing it to a component as a child or a prop raises `BackendVarFormatError` (earlier releases may instead raise `ChildrenTypeError` for a child, `TypeError` for a typed prop, or `TypeError: Unsupported type <class 'reflex_base.vars.base.Field'> for LiteralVar` when the value is built or rendered). Turning it into text with an f-string, `str.format()`, `str()` or `%` raises `BackendVarFormatError` (earlier 0.10 releases embed the `Field` text for `!s`, `str()` and `%`; `!r` and `repr()` still do). Reading the var on a state instance, such as `self._items`, is unchanged.
Replace the class read with what the code meant:
</file context>
| ) | ||
| with pytest.raises( | ||
| exceptions.BackendVarFormatError, | ||
| match=r"Backend var 'BackendMetaState\._meta' exists only", |
There was a problem hiding this comment.
P3: The new pytest.raises calls match only a fragment of the error message via a partial regex. Per AGENTS.md, assert the full error message exactly, so a changed or truncated message is caught. Please match the complete expected text in both new tests.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/units/test_app.py, line 352:
<comment>The new `pytest.raises` calls match only a fragment of the error message via a partial regex. Per `AGENTS.md`, assert the full error message exactly, so a changed or truncated message is caught. Please match the complete expected text in both new tests.</comment>
<file context>
@@ -333,6 +333,46 @@ def test_add_page_backend_var_meta_raises_format_error(
+ )
+ with pytest.raises(
+ exceptions.BackendVarFormatError,
+ match=r"Backend var 'BackendMetaState\._meta' exists only",
+ ):
+ app._compile_page("meta")
</file context>
| S.on_event(S._secret) | ||
| with pytest.raises(EventHandlerTypeError, match=r"Got Field\(default=42"): | ||
| cast(EventSpec, S.on_event()).add_args(S._secret) # pyright: ignore[reportArgumentType] | ||
| match = r"Backend var 'BackendEventArgState\._secret' exists only" |
There was a problem hiding this comment.
P3: This match is a partial regex, so the test passes for any message starting with this prefix. AGENTS.md asks for the full error message to be compared exactly. Match the complete BackendVarFormatError text from _no_frontend_var_error, with the owner and field names fixed.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/units/test_event.py, line 278:
<comment>This `match` is a partial regex, so the test passes for any message starting with this prefix. AGENTS.md asks for the full error message to be compared exactly. Match the complete `BackendVarFormatError` text from `_no_frontend_var_error`, with the owner and field names fixed.</comment>
<file context>
@@ -266,20 +265,23 @@ def args_spec(value: Var[dict]) -> list[Var[dict]]:
- S.on_event(S._secret)
- with pytest.raises(EventHandlerTypeError, match=r"Got Field\(default=42"):
- cast(EventSpec, S.on_event()).add_args(S._secret) # pyright: ignore[reportArgumentType]
+ match = r"Backend var 'BackendEventArgState\._secret' exists only"
+ with pytest.raises(BackendVarFormatError, match=match):
+ BackendEventArgState.on_event(BackendEventArgState._secret)
</file context>
| ## Printing a backend var on a state class | ||
|
|
||
| A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, logging and `str()` of it raise `BackendVarFormatError`, as f-strings already did ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component prop, `rx.toast(State._secret)`, or the `title`, `description` or `image` of `app.add_page(...)`. | ||
| A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, logging and `str()` of it raise `BackendVarFormatError`, as f-strings already did ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component child or prop, an event handler argument, `rx.toast(State._secret)`, or the `title`, `description`, `image` or `on_load` of `app.add_page(...)`. Comparing it with a value, as in `rx.cond(State._secret == "x", ...)`, or truth-testing it also raises, where it used to give a plain `False` or `True`. |
There was a problem hiding this comment.
P3: Logging does not raise here. With %-style arguments, the stdlib logging handler catches the error and prints a --- Logging error --- report. Name print() and str() as the raising cases, and say that repr() is the way to log the field.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/changelog/upgrading/upgrading-to-0-11.md, line 244:
<comment>Logging does not raise here. With %-style arguments, the stdlib logging handler catches the error and prints a `--- Logging error ---` report. Name `print()` and `str()` as the raising cases, and say that `repr()` is the way to log the field.</comment>
<file context>
@@ -241,7 +241,7 @@ def serialize_point(point: Point) -> dict:
## Printing a backend var on a state class
-A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, logging and `str()` of it raise `BackendVarFormatError`, as f-strings already did ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component prop, `rx.toast(State._secret)`, or the `title`, `description` or `image` of `app.add_page(...)`.
+A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, logging and `str()` of it raise `BackendVarFormatError`, as f-strings already did ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component child or prop, an event handler argument, `rx.toast(State._secret)`, or the `title`, `description`, `image` or `on_load` of `app.add_page(...)`. Comparing it with a value, as in `rx.cond(State._secret == "x", ...)`, or truth-testing it also raises, where it used to give a plain `False` or `True`.
```python
</file context>
| A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, logging and `str()` of it raise `BackendVarFormatError`, as f-strings already did ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component child or prop, an event handler argument, `rx.toast(State._secret)`, or the `title`, `description`, `image` or `on_load` of `app.add_page(...)`. Comparing it with a value, as in `rx.cond(State._secret == "x", ...)`, or truth-testing it also raises, where it used to give a plain `False` or `True`. | |
| A backend var read on the state class, such as `State._secret`, is a `Field` since 0.10. In 0.11, `print()`, `str()` and f-strings of it raise `BackendVarFormatError` ([#7651](https://github.com/reflex-dev/reflex/pull/7651)). Logging it with an f-string raises too, but logging with `%s` arguments does not raise to the caller. The same error replaces the `TypeError` or `ValueError` raised when the var is passed where a frontend value is needed, such as a component child or prop, an event handler argument, `rx.toast(State._secret)`, or the `title`, `description`, `image` or `on_load` of `app.add_page(...)`. Comparing it with a value, as in `rx.cond(State._secret == "x", ...)`, or truth-testing it also raises, where it used to give a plain `False` or `True`. |
| with pytest.raises(ChildrenTypeError, match=r"received child of type Field\."): | ||
| with pytest.raises( | ||
| BackendVarFormatError, | ||
| match=r"Backend var 'BackendChildState\._secret' exists only", |
There was a problem hiding this comment.
P3: This prefix-only match passes even if the rest of the error, including its suggested fix, changes. Assert the complete expected message here and in the other new backend-var error checks.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/units/components/test_component.py, line 799:
<comment>This prefix-only `match` passes even if the rest of the error, including its suggested fix, changes. Assert the complete expected message here and in the other new backend-var error checks.</comment>
<file context>
@@ -789,12 +789,15 @@ def test_component_create_unallowed_types(children, test_component):
- with pytest.raises(ChildrenTypeError, match=r"received child of type Field\."):
+ with pytest.raises(
+ BackendVarFormatError,
+ match=r"Backend var 'BackendChildState\._secret' exists only",
+ ):
test_component.create(BackendChildState._secret)
</file context>
Merging this PR will degrade performance by 6.13%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
All Submissions:
Type of change
Changes To Core Features:
Description
Closes #7843.
Every row in the issue now raises the same
BackendVarFormatErroras the other UI paths, and none of them prints the default value:Fieldbefore the generic checks. Themetarow goes through the typed prop check, so it raises when the page compiles.LiteralVar.createalready builds, instead of wrapping it inEventHandlerTypeError.add_pagecheckson_load, the same way it checkstitle,descriptionandimage.Field.__eq__raises when a backend var is compared with something that isn't a field, and__bool__raises for a backend var. Field-to-field comparison stays identity, and__hash__staysobject.__hash__, so dict keys, sets andinchecks over fields work as before.The comparison and bool checks also cover a mixin's fields, since formatting one already raises the mixin message. I kept the check off ordering (
State._n > 0) andin, which already fail with a plainTypeErrorrather than silently.Tests: one per row, plus mixins, field identity and hashing. The 13 new or changed tests fail on main and pass with the fix. The full unit suite has the same failures as main (only the
pyi_generatortests, which fail on my machine with or without this change). I also updated the two upgrading guides that described the old errors.