From 919741f846b6f5015a33d03967e9ee7a3c966c78 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 02:02:12 +0000 Subject: [PATCH 1/3] Fix get_var_value silently returning wrong values for operation vars get_var_value resolved a var's state/field_name via _get_all_var_data(), which recursively merges the var-data of every constituent var. For an operation/derived var (e.g. State.a + State.b or State.items[0]) the merge back-filled state/field_name from the first operand, so get_var_value returned that operand's value instead of the operation's result, silently and without error. Resolve via the underlying var's own _var_data instead: unwrap any cast (ToOperation) wrappers and require the inner var to carry both state and field_name (a plain field or computed-var reference). Operation/derived vars now raise UnretrievableVarValueError rather than returning a plausible-but-wrong value, matching the documented behavior. Fixes #6629 --- reflex/state.py | 15 +++++++++++++-- tests/units/test_state.py | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/reflex/state.py b/reflex/state.py index e3e959a2d44..e7c96018654 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -59,6 +59,7 @@ ComputedVar, DynamicRouteVar, EvenMoreBasicBaseState, + ToOperation, Var, computed_var, dispatch, @@ -1747,8 +1748,18 @@ async def get_var_value(self, var: Var[VAR_TYPE]) -> VAR_TYPE: ) is not unset and not isinstance(var_value, Var): return var_value # pyright: ignore [reportReturnType] - var_data = var._get_all_var_data() - if var_data is None or not var_data.state: + # Unwrap any cast wrappers and resolve via the underlying var's *own* + # var data, not the recursive _get_all_var_data(). For an operation or + # derived var (e.g. State.a + State.b or State.items[0]), the recursive + # merge back-fills state/field_name from the first operand, which would + # make us silently return that operand's value instead of the operation's + # result. Only a plain field or computed var reference carries + # state + field_name on its own var data. + inner_var = var + while isinstance(inner_var, ToOperation): + inner_var = inner_var._original + var_data = inner_var._var_data + if var_data is None or not var_data.state or not var_data.field_name: msg = f"Unable to retrieve value for {var._js_expr}: not associated with any state." raise UnretrievableVarValueError(msg) # Fastish case: this var belongs to this state diff --git a/tests/units/test_state.py b/tests/units/test_state.py index f793c7af022..35ac8439276 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -4575,6 +4575,20 @@ async def test_get_var_value( "b": [4, 5, 6], } + # Regression for https://github.com/reflex-dev/reflex/issues/6629: a Var + # operation / derived var (arithmetic, indexed or item access) must not + # silently return the value of its first constituent field. Such vars have + # no retrievable value, so raise instead of returning a plausible-but-wrong one. + with pytest.raises(UnretrievableVarValueError): + await state.get_var_value(TestState.num1 + TestState.num2) + with pytest.raises(UnretrievableVarValueError): + await state.get_var_value(TestState.array[0]) + with pytest.raises(UnretrievableVarValueError): + await state.get_var_value(TestState.mapping["a"]) + + # Computed vars are derived but state-bound, so they remain resolvable. + assert await state.get_var_value(TestState.sum) == pytest.approx(42 + 3.15) + @pytest.mark.asyncio async def test_get_var_value_async_computed_var( From f7005e1e5b8485f85ffa69f1b4ab7d1190ce334b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 02:38:24 +0000 Subject: [PATCH 2/3] Fix pyright error in get_var_value regression test TestState.array is a plain list[float] annotation, so pyright statically infers array[0] as float rather than a Var. Annotate the line with a justified pyright ignore (the expression is a Var operation at runtime). --- tests/units/test_state.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/units/test_state.py b/tests/units/test_state.py index 35ac8439276..83aa823598a 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -4582,7 +4582,8 @@ async def test_get_var_value( with pytest.raises(UnretrievableVarValueError): await state.get_var_value(TestState.num1 + TestState.num2) with pytest.raises(UnretrievableVarValueError): - await state.get_var_value(TestState.array[0]) + # array[0] is a Var operation at runtime, though statically typed as the element. + await state.get_var_value(TestState.array[0]) # pyright: ignore[reportArgumentType] with pytest.raises(UnretrievableVarValueError): await state.get_var_value(TestState.mapping["a"]) From b74e7b0d10c3917a76fc409bfabcf860275c3c1a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 02:39:52 +0000 Subject: [PATCH 3/3] Add changelog fragment for #6633 --- news/6633.bugfix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/6633.bugfix.md diff --git a/news/6633.bugfix.md b/news/6633.bugfix.md new file mode 100644 index 00000000000..f29a7e3d0b6 --- /dev/null +++ b/news/6633.bugfix.md @@ -0,0 +1 @@ +`State.get_var_value()` no longer silently returns a wrong value when passed a Var operation — an arithmetic/concatenation expression such as `State.a + State.b`, or an indexed/item access such as `State.items[0]`. Previously it resolved the state and field of the operation's *first* operand and returned that field's value instead of the operation's result. It now raises `UnretrievableVarValueError`, consistent with how it already handled vars not associated with any state. Plain field and computed-var references continue to resolve as before.