From 4dc20c6be404dacdac4d58f6741985b22c87e0b1 Mon Sep 17 00:00:00 2001 From: Evan Mattson <35585003+moonbox3@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:46:16 +0900 Subject: [PATCH] Python: Fix PowerFx eval crash on non-English system locales by setting CurrentUICulture to en-US (#4408) * Fix #4321: Set CurrentUICulture to en-US in PowerFx eval() On non-English systems, CultureInfo.CurrentUICulture causes PowerFx to emit localized error messages. The existing ValueError guard only matches English strings ("isn't recognized", "Name isn't valid"), so undefined variable errors crash instead of returning None gracefully. Fix: save and restore CurrentUICulture alongside CurrentCulture before calling engine.eval(), ensuring error messages are always in English. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Reuse single CultureInfo instance to avoid redundant allocations Cache CultureInfo("en-US") in a local variable instead of instantiating it twice per eval() call, as suggested in PR review. Fixes #4321 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add assertion for CurrentUICulture restoration after eval Assert that the production code's finally-block correctly restores CurrentUICulture to it-IT after eval returns, covering future regressions where the culture could leak. The CultureInfo caching suggestion (comment #2) was already implemented in the production code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../_workflows/_declarative_base.py | 6 ++++- .../tests/test_powerfx_yaml_compatibility.py | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/python/packages/declarative/agent_framework_declarative/_workflows/_declarative_base.py b/python/packages/declarative/agent_framework_declarative/_workflows/_declarative_base.py index 687dad096b..01a68e6a8e 100644 --- a/python/packages/declarative/agent_framework_declarative/_workflows/_declarative_base.py +++ b/python/packages/declarative/agent_framework_declarative/_workflows/_declarative_base.py @@ -388,11 +388,15 @@ class DeclarativeWorkflowState: from System.Globalization import CultureInfo original_culture = CultureInfo.CurrentCulture - CultureInfo.CurrentCulture = CultureInfo("en-US") + original_ui_culture = CultureInfo.CurrentUICulture + en_us_culture = CultureInfo("en-US") + CultureInfo.CurrentCulture = en_us_culture + CultureInfo.CurrentUICulture = en_us_culture try: return engine.eval(formula, symbols=symbols) finally: CultureInfo.CurrentCulture = original_culture + CultureInfo.CurrentUICulture = original_ui_culture except ValueError as e: error_msg = str(e) # Handle undefined variable errors gracefully by returning None diff --git a/python/packages/declarative/tests/test_powerfx_yaml_compatibility.py b/python/packages/declarative/tests/test_powerfx_yaml_compatibility.py index 9591dc05cb..8ea3c3af57 100644 --- a/python/packages/declarative/tests/test_powerfx_yaml_compatibility.py +++ b/python/packages/declarative/tests/test_powerfx_yaml_compatibility.py @@ -493,6 +493,31 @@ class TestPowerFxUndefinedVariables: result = state.eval("=Local.Something.Nested.Deep") assert result is None + async def test_undefined_variable_returns_none_with_non_english_ui_culture(self, mock_state): + """Test that undefined variables return None even when CurrentUICulture is non-English. + + Regression test for #4321: on non-English systems, CurrentUICulture causes + PowerFx to emit localized error messages that don't match the English + string guards ("isn't recognized", "Name isn't valid"), crashing the workflow. + The fix sets CurrentUICulture to en-US alongside CurrentCulture before eval. + """ + from System.Globalization import CultureInfo + + state = DeclarativeWorkflowState(mock_state) + state.initialize() + + # Simulate a non-English UI culture (e.g. Italian) + original_ui_culture = CultureInfo.CurrentUICulture + CultureInfo.CurrentUICulture = CultureInfo("it-IT") + try: + # Should return None, not raise ValueError with Italian error text + result = state.eval("=Local.StatusConversationId") + assert result is None + # Verify the production code restored CurrentUICulture after eval + assert str(CultureInfo.CurrentUICulture) == str(CultureInfo("it-IT")) + finally: + CultureInfo.CurrentUICulture = original_ui_culture + class TestStringInterpolation: """Test string interpolation patterns."""