From 71d3438d2a82b2d59ef5425713a2ae2bd46ebf87 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 8 Apr 2026 10:14:21 -0300 Subject: [PATCH] fix: frozen vertices crash with TypeError when no cache service available (#12409) * fix: return CacheMiss() from fallback cache func so frozen vertices rebuild when no cache service Fixes #12408 * fix: address review feedback on frozen vertex fix - Use graph.arun() in test to exercise the process() path - Make set_cache_func in process() return True for consistency with astep() * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/lfx/src/lfx/graph/graph/base.py | 8 ++-- .../tests/unit/graph/test_frozen_vertex.py | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/lfx/tests/unit/graph/test_frozen_vertex.py diff --git a/src/lfx/src/lfx/graph/graph/base.py b/src/lfx/src/lfx/graph/graph/base.py index 7b0ca83fa4..e08e772063 100644 --- a/src/lfx/src/lfx/graph/graph/base.py +++ b/src/lfx/src/lfx/graph/graph/base.py @@ -1457,7 +1457,7 @@ class Graph: else: # Fallback no-op cache functions for tests or when service unavailable async def get_cache_func(*args, **kwargs): # noqa: ARG001 - return None + return CacheMiss() async def set_cache_func(*args, **kwargs) -> bool: # noqa: ARG001 return True @@ -1682,10 +1682,10 @@ class Graph: else: # Fallback no-op cache functions for tests or when service unavailable async def get_cache_func(*args, **kwargs): # noqa: ARG001 - return None + return CacheMiss() - async def set_cache_func(*args, **kwargs): - pass + async def set_cache_func(*args, **kwargs) -> bool: # noqa: ARG001 + return True await self.initialize_run() lock = asyncio.Lock() diff --git a/src/lfx/tests/unit/graph/test_frozen_vertex.py b/src/lfx/tests/unit/graph/test_frozen_vertex.py new file mode 100644 index 0000000000..8b6dea6bbc --- /dev/null +++ b/src/lfx/tests/unit/graph/test_frozen_vertex.py @@ -0,0 +1,46 @@ +import copy +import json +from pathlib import Path + +import pytest +from lfx.graph import Graph + + +@pytest.fixture +def simple_chat_flow(): + """Load the simple chat JSON test data.""" + test_data_dir = Path(__file__).parent.parent.parent / "data" + json_path = test_data_dir / "simple_chat_no_llm.json" + with json_path.open() as f: + return json.load(f) + + +@pytest.fixture +def frozen_chat_flow(simple_chat_flow): + """Create a flow with a frozen vertex (ChatOutput).""" + flow = copy.deepcopy(simple_chat_flow) + for node in flow["data"]["nodes"]: + if node["data"]["node"].get("display_name") == "Chat Output": + node["data"]["node"]["frozen"] = True + return flow + + +@pytest.mark.asyncio +async def test_frozen_vertex_rebuilds_when_no_cache_service(frozen_chat_flow): + """A frozen vertex should rebuild gracefully when no cache service is available. + + When running standalone (no server), chat_service is None. The fallback + get_cache_func should return CacheMiss so frozen vertices fall through + to the build path instead of crashing with TypeError. + + Reproduces: https://github.com/langflow-ai/langflow/issues/12408 + """ + graph = Graph.from_payload(frozen_chat_flow) + + # Verify the vertex is actually frozen + frozen_vertices = [v for v in graph.vertices if v.frozen] + assert len(frozen_vertices) > 0, "Expected at least one frozen vertex" + + # Use arun which goes through process(), the same path as arun_flow_from_json + results = await graph.arun(inputs=[{"input_value": "hello"}]) + assert len(results) > 0