From 050ebd65f7225af2cb1e557ed2fc8df4d47fd962 Mon Sep 17 00:00:00 2001 From: Anish Gurjar <50738829+anishgurjar@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:14:31 -0400 Subject: [PATCH 1/4] fix(langchain): deregister langfuse prompts when a chain run errors --- langfuse/langchain/CallbackHandler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/langfuse/langchain/CallbackHandler.py b/langfuse/langchain/CallbackHandler.py index 65ce1b2d8..0db58cd6e 100644 --- a/langfuse/langchain/CallbackHandler.py +++ b/langfuse/langchain/CallbackHandler.py @@ -852,6 +852,8 @@ def on_chain_error( except Exception as e: langfuse_logger.exception(e) finally: + self._deregister_langfuse_prompt(run_id) + if parent_run_id is None: self._exit_propagation_context(run_id) self._reset(run_id) From 377e95e82b63f3dc22ce9d853ffee74afe42a86f Mon Sep 17 00:00:00 2001 From: Anish Gurjar <50738829+anishgurjar@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:15:10 -0400 Subject: [PATCH 2/4] test(langchain): cover prompt deregistration on chain error --- tests/unit/test_langchain.py | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/unit/test_langchain.py b/tests/unit/test_langchain.py index dbbb3d93b..6b6143fcc 100644 --- a/tests/unit/test_langchain.py +++ b/tests/unit/test_langchain.py @@ -401,6 +401,82 @@ def test_streamed_llm_error_cleans_only_its_completion_start_time_memo( ) +def test_chain_error_deregisters_prompt_registered_for_the_run( + langfuse_memory_client, +): + handler = CallbackHandler() + + root_run_id = uuid4() + prompt_chain_run_id = uuid4() + + handler.on_chain_start( + {"name": "RootChain"}, + {"input": "hi"}, + run_id=root_run_id, + ) + handler.on_chain_start( + {"name": "PromptChain"}, + {"input": "hi"}, + run_id=prompt_chain_run_id, + parent_run_id=root_run_id, + metadata={"langfuse_prompt": "sentinel-prompt"}, + ) + + assert handler._prompt_to_parent_run_map.get(root_run_id) == "sentinel-prompt" + + handler.on_chain_error( + RuntimeError("synthetic failure"), + run_id=root_run_id, + ) + + assert root_run_id not in handler._prompt_to_parent_run_map + + +def test_nested_chain_error_deregisters_propagated_prompt( + langfuse_memory_client, +): + handler = CallbackHandler() + + root_run_id = uuid4() + prompt_chain_run_id = uuid4() + nested_chain_run_id = uuid4() + + handler.on_chain_start( + {"name": "RootChain"}, + {"input": "hi"}, + run_id=root_run_id, + ) + handler.on_chain_start( + {"name": "PromptChain"}, + {"input": "hi"}, + run_id=prompt_chain_run_id, + parent_run_id=root_run_id, + metadata={"langfuse_prompt": "sentinel-prompt"}, + ) + handler.on_chain_start( + {"name": "NestedChain"}, + {"input": "hi"}, + run_id=nested_chain_run_id, + parent_run_id=root_run_id, + ) + + assert ( + handler._prompt_to_parent_run_map.get(nested_chain_run_id) == "sentinel-prompt" + ) + + handler.on_chain_error( + RuntimeError("synthetic failure"), + run_id=nested_chain_run_id, + parent_run_id=root_run_id, + ) + + assert nested_chain_run_id not in handler._prompt_to_parent_run_map + + # The parent-level registration survives so a following LLM sibling + # can still consume it, matching the success-path lifecycle. + assert handler._prompt_to_parent_run_map.get(root_run_id) == "sentinel-prompt" + + def test_control_flow_resume_uses_thread_keyed_explicit_resume_context( memory_exporter, langfuse_memory_client, monkeypatch ): From 12180f32c91f65f74664bebda6769cd5aded986f Mon Sep 17 00:00:00 2001 From: Anish Gurjar <50738829+anishgurjar@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:16:22 -0400 Subject: [PATCH 3/4] style: trim trailing blank line for ruff format From 3ab821ccff7d81cf739b18a818ce3b4cfc9db3c3 Mon Sep 17 00:00:00 2001 From: Anish Gurjar <50738829+anishgurjar@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:17:14 -0400 Subject: [PATCH 4/4] style: trim trailing blank line for ruff format