langchain - ✅(Solved) Fix RunnableWithFallbacks.invoke() drops parent_run_id for child callback events (regression from #25550) [14 pull requests, 10 comments, 9 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#36072Fetched 2026-04-08 00:58:16
View on GitHub
Comments
10
Participants
9
Timeline
36
Reactions
0
Timeline (top)
cross-referenced ×14commented ×9referenced ×7labeled ×3

RunnableWithFallbacks.invoke() passes config instead of child_config to child runnables (line 196 of fallbacks.py). This causes child callback events (on_chat_model_start, on_llm_error, on_llm_end) to receive parent_run_id=None instead of the fallback chain's run_id.

This breaks any tracing/observability tool that relies on parent_run_id to build span trees — child LLM spans become orphans invisible to the trace.

Regression introduced by #25550. Fix is one line — change config to child_config:

                child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:
                    output = context.run(
                        runnable.invoke,
                        input,
-                        config,
+                        child_config,
                        **kwargs,
                    )

Same issue likely applies to ainvoke, batch, and abatch.

Error Message

from langchain_openai import ChatOpenAI
from langchain_core.callbacks.base import BaseCallbackHandler from langchain_core.messages import HumanMessage

class Tracker(BaseCallbackHandler):
def init(self): self.events = []

def on_chain_start(self, serialized, inputs, *, run_id, parent_run_id=None, **kwargs):
    self.events.append(("chain_start", run_id, parent_run_id))

def on_chat_model_start(self, serialized, messages, *, run_id, parent_run_id=None, **kwargs):
    self.events.append(("chat_model_start", run_id, parent_run_id))

def on_llm_error(self, error, *, run_id, parent_run_id=None, **kwargs):
    self.events.append(("llm_error", run_id, parent_run_id))

tracker = Tracker() llm = ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad").with_fallbacks( [ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad-2")] )

try: llm.invoke([HumanMessage(content="hi")], config={"callbacks": [tracker]}) except Exception: pass

chain_run_id = tracker.events[0][1] # run_id from chain_start for event_name, run_id, parent_run_id in tracker.events: if event_name in ("chat_model_start", "llm_error"): assert parent_run_id == chain_run_id, ( f"{event_name}: expected parent_run_id={chain_run_id}, got {parent_run_id}" )

Root Cause

RunnableWithFallbacks.invoke() passes config instead of child_config to child runnables (line 196 of fallbacks.py). This causes child callback events (on_chat_model_start, on_llm_error, on_llm_end) to receive parent_run_id=None instead of the fallback chain's run_id.

This breaks any tracing/observability tool that relies on parent_run_id to build span trees — child LLM spans become orphans invisible to the trace.

Regression introduced by #25550. Fix is one line — change config to child_config:

                child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:
                    output = context.run(
                        runnable.invoke,
                        input,
-                        config,
+                        child_config,
                        **kwargs,
                    )

Same issue likely applies to ainvoke, batch, and abatch.

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Regression introduced by #25550. Fix is one line — change config to child_config:

                child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:
                    output = context.run(
                        runnable.invoke,
                        input,
-                        config,
+                        child_config,
                        **kwargs,
                    )

Same issue likely applies to ainvoke, batch, and abatch.

Other Dependencies

bottleneck: 1.6.0 google-cloud-aiplatform: 1.139.0 google-cloud-storage: 3.9.0 google-cloud-vectorsearch: 0.5.0 httpx: 0.28.1 httpx-sse: 0.4.3 jsonpatch: 1.33 langgraph: 1.0.10 numexpr: 2.14.1 numpy: 2.4.2 openai: 2.29.0 openai-agents: 0.12.4 opentelemetry-api: 1.39.1 opentelemetry-exporter-otlp-proto-http: 1.39.1 opentelemetry-sdk: 1.39.1 orjson: 3.11.7 packaging: 26.0 pyarrow: 22.0.0 pydantic: 2.12.5 pytest: 9.0.2 pytest-asyncio: 1.3.0 pytest-benchmark: 5.2.3 pytest-codspeed: 4.3.0 pytest-recording: 0.13.4 pytest-socket: 0.7.0 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 rich: 14.3.3 syrupy: 5.1.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 validators: 0.35.0 vcrpy: 8.1.1 websockets: 16.0 wrapt: 1.17.3 xxhash: 3.6.0 zstandard: 0.25.0

PR fix notes

PR #36075: fix(core): pass child_config to child runnables in RunnableWithFallbacks

Description (problem / solution / changelog)

Description

Fixes #36072

RunnableWithFallbacks.invoke(), ainvoke(), and stream() pass the parent config instead of child_config to child runnables. This causes child callback events (on_chain_start, on_chain_error, on_chain_end) to receive parent_run_id=None instead of the fallback chain's run_id, breaking tracing/observability tools that rely on parent_run_id to build span trees.

This regression was introduced by #25550, which was intended to fix #25337 but passed config where child_config should have been used.

Changes

libs/core/langchain_core/runnables/fallbacks.py:

  • invoke() (line 196): config -> child_config
  • ainvoke() (line 247): config -> child_config
  • stream() (line 497): added missing child_config argument to runnable.stream() call

Note: batch(), abatch(), and astream() already correctly pass child configs.

libs/core/tests/unit_tests/runnables/test_fallbacks.py:

  • Added test_invoke_child_callbacks_receive_parent_run_id
  • Added test_ainvoke_child_callbacks_receive_parent_run_id
  • Added test_stream_child_callbacks_receive_parent_run_id

Each test verifies that all child events have parent_run_id equal to the root chain's run_id.

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +3/-2)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py (modified, +117/-0)

PR #36077: fix(core): pass child_config in RunnableWithFallbacks invoke/ainvoke

Description (problem / solution / changelog)

Fixes #36072 When using a fallback callback, the fallback callback config isn't passed to child spans. This makes the parent as None. The fix passes child_config to the child spans.

Social handles (optional)

<!-- If you'd like a shoutout on release, add your socials below -->

Twitter: @ LinkedIn: https://www.linkedin.com/in/ankush-m/

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)

PR #36079: fix(core): preserve parent callback lineage in fallbacks

Description (problem / solution / changelog)

Fixes #36072

Pass the patched child callback config into fallback child runnables so callback events keep the fallback run as parent_run_id. Adds focused sync and async regression tests covering invoke() and ainvoke().

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py (modified, +41/-0)

PR #36081: fix: pass child_config instead of config to child runnables in invoke/ainvoke

Description (problem / solution / changelog)

Summary

Fixes regression from #25550 where parent_run_id was dropped for child callback events.

Problem

RunnableWithFallbacks.invoke() was passing config instead of child_config to child runnables (lines 196 and 247 of fallbacks.py). This causes child callback events (on_chat_model_start, on_llm_error, etc.) to receive parent_run_id=None instead of the fallback chain's run_id.

This breaks any tracing/observability tool that relies on parent_run_id to build span trees — child LLM spans become orphans invisible to the trace.

Changes

  • Line 196: Changed configchild_config in invoke()
  • Line 247: Changed configchild_config in ainvoke()

Note: batch() and abatch() were already correctly using patched configs.

Testing

The fix follows the exact pattern suggested in issue #36072. The batch and abatch methods already work correctly because they properly construct child configs inline.

Fixes: langchain-ai/langchain#36072

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/core/langchain_core/utils/_merge.py (modified, +1/-1)
  • libs/core/langchain_core/utils/usage.py (modified, +3/-3)

PR #36091: fix: pass child_config instead of config in RunnableWithFallbacks.invoke/ainvoke

Description (problem / solution / changelog)

Summary

Fixes regression introduced by #25550 where child callback events (on_chat_model_start, on_llm_error, on_llm_end) receive parent_run_id=None instead of the fallback chain's run_id.

This breaks tracing/observability tools that rely on parent_run_id to build span trees.

Changes

  • invoke: pass child_config to runnable.invoke
  • ainvoke: pass child_config to runnable.ainvoke

Note: batch and abatch were already correct.

Related Issues

  • Fixes #36072
  • Regression from #25550

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/core/langchain_core/utils/_merge.py (modified, +1/-1)
  • libs/core/langchain_core/utils/usage.py (modified, +9/-8)
  • libs/partners/anthropic/langchain_anthropic/middleware/anthropic_tools.py (modified, +2/-2)

PR #36094: fix: RunnableWithFallbacks passes child_config to child runnables

Description (problem / solution / changelog)

Summary

Fixes a regression where RunnableWithFallbacks.invoke() was passing config instead of child_config to child runnables, causing child callback events to receive parent_run_id=None instead of the fallback chain's run_id.

Changes

  • Fixed configchild_config in invoke() method
  • Fixed configchild_config in ainvoke() method

Testing

  • Verified child callback events now receive correct parent_run_id
  • Syntax validation passed

Fixes langchain-ai/langchain#36072

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)

PR #36107: fix: pass child_config in RunnableWithFallbacks invoke/ainvoke

Description (problem / solution / changelog)

Summary

Fixes #36072

PR #25550 introduced a regression where RunnableWithFallbacks.invoke() and ainvoke() pass config (the parent config) instead of child_config to child runnables, causing parent_run_id to be lost in callback events.

  • In invoke(): changed context.run(runnable.invoke, input, config, ...) to context.run(runnable.invoke, input, child_config, ...)
  • In ainvoke(): changed context.run(runnable.ainvoke, input, config, ...) to context.run(runnable.ainvoke, input, child_config, ...)

This restores the correct callback hierarchy by ensuring child runnables receive child_config (which includes the proper parent_run_id from run_manager.get_child()), consistent with how batch(), abatch(), and astream() already work.

Test plan

  • Verify invoke() passes child_config to child runnables
  • Verify ainvoke() passes child_config to child runnables
  • Confirmed batch(), abatch(), and astream() already use child_config correctly
  • Existing unit tests for RunnableWithFallbacks should continue to pass

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)

PR #36122: fix(core): pass child_config instead of config in RunnableWithFallbacks.invoke()

Description (problem / solution / changelog)

In RunnableWithFallbacks.invoke(), child_config is created with the correct child callbacks via patch_config(config, callbacks=run_manager.get_child()), but then config (the parent) is passed to runnable.invoke instead of child_config.

This causes child callback events (on_chat_model_start, on_llm_error, on_llm_end) to receive parent_run_id=None instead of the fallback chain run_id, breaking tracing/observability tools that rely on parent_run_id to build span trees.

Fix: change config to child_config in the context.run() call.

Closes #36072

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +1/-1)

PR #36148: fix(core): #36072 pass child_config to runnable.invoke/ainvoke to preserve parent_run_id

Description (problem / solution / changelog)

Summary

Fixes #36072 - RunnableWithFallbacks.invoke() drops parent_run_id

Changes

In libs/core/langchain_core/runnables/fallbacks.py, the invoke() and invoke() methods were passing the parent config instead of child_config to unnable.invoke() / unnable.ainvoke(). This caused child callback events to have parent_run_id = None, breaking trace trees.

Fix

  • Line 196: config, → child_config, in invoke()
  • Line 247: config, → child_config, in invoke()

Testing

  • Existing tests in est_fallbacks.py cover this functionality
  • Manual verification confirms child runs now correctly inherit parent_run_id

@copilot

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)

PR #36151: fix(core): pass child_config instead of config to fallback runnables in RunnableWithFallbacks

Description (problem / solution / changelog)

Problem

Fixes #36072

In RunnableWithFallbacks.invoke() and ainvoke(), the fallback runnable is called with the parent config instead of child_config:

# invoke() line ~189
child_config = patch_config(config, callbacks=run_manager.get_child())
with set_config_context(child_config) as context:
    output = context.run(
        runnable.invoke,
        input,
        config,        # ← Bug: should be child_config
        **kwargs,
    )

Because the fallback runnable receives config (parent) rather than child_config, it never sees:

  • The parent_run_id set by run_manager.get_child()
  • The callback chain attached to the child run

All callback events emitted by the fallback runnable appear as top-level, parentless runs, breaking tracing, LangSmith, streaming, and any handler that relies on the run hierarchy.

Root Cause

Regression introduced by #25550 (which fixed #25337). That PR added set_config_context(child_config) to fix callbacks but kept the original config argument when calling runnable.invoke, negating part of the benefit.

Fix

Pass child_config (not config) to both runnable.invoke() and runnable.ainvoke() inside the fallback loop:

output = context.run(runnable.invoke, input, child_config, **kwargs)  # sync
coro   = context.run(runnable.ainvoke, input, child_config, **kwargs)  # async

Testing

from langchain_core.callbacks.base import BaseCallbackHandler
from langchain_core.runnables import RunnableLambda

parent_run_ids = []

class TrackParent(BaseCallbackHandler):
    def on_chain_start(self, *args, parent_run_id=None, **kwargs):
        parent_run_ids.append(parent_run_id)

error = RunnableLambda(lambda _: (_ for _ in ()).throw(ValueError("fail")))
fallback = RunnableLambda(lambda _: "ok")
chain = error.with_fallbacks([fallback])
chain.invoke("test", config={"callbacks": [TrackParent()]})

assert parent_run_ids[1] is not None  # fallback run now has a parent

Changed files

  • docs/docs/integrations/chat/gigachat.ipynb (added, +141/-0)
  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/langchain/langchain/chat_models/base.py (modified, +10/-0)
  • libs/langchain/pyproject.toml (modified, +1/-0)

PR #36159: fix(core): pass child_config in RunnableWithFallbacks invoke/ainvoke/stream

Description (problem / solution / changelog)

Description

Fixes the regression introduced by #25550 where RunnableWithFallbacks passes the parent config to child runnables instead of the already-prepared child_config. This causes all child callback events to receive parent_run_id=None, breaking tracing/observability tools that rely on the parent → child run-id relationship to build span trees.

Root cause: Three methods pass the wrong config:

MethodBug
invoke()Passes config instead of child_config (line 196)
ainvoke()Passes config instead of child_config (line 247)
stream()Passes no config to runnable.stream (line 496-499)

astream already passes child_config correctly; batch/abatch also construct and pass child configs correctly.

Issue

Fixes #36072

Changes

  • libs/core/langchain_core/runnables/fallbacks.py: 3 one-line fixes (config → child_config)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py: regression tests for invoke, ainvoke, stream

Test plan

Added 3 regression tests that verify child runs carry the correct parent_run_id after a fallback fires:

tests/unit_tests/runnables/test_fallbacks.py::test_fallbacks_invoke_preserves_parent_run_id  PASSED
tests/unit_tests/runnables/test_fallbacks.py::test_fallbacks_ainvoke_preserves_parent_run_id PASSED
tests/unit_tests/runnables/test_fallbacks.py::test_fallbacks_stream_preserves_parent_run_id  PASSED

All 19 existing fallback tests still pass.

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +3/-2)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py (modified, +102/-1)

PR #36182: fix(core): preserve parent_run_id in fallback invoke paths

Description (problem / solution / changelog)

Summary

  • pass child_config into fallback invoke() and ainvoke() child runnables
  • add focused fallback callback regression tests for both sync and async invocation paths

Why

RunnableWithFallbacks already builds a patched child_config with callbacks=run_manager.get_child(), but invoke() and ainvoke() were still calling child runnables with the original config. That drops parent_run_id on fallback child callback events, which makes tracing/observability trees lose the child span relationship.

batch, abatch, and astream already use patched child configs correctly. This change brings the sync and async invoke paths back in line with the existing callback contract.

Validation

  • uv run ruff format --check langchain_core/runnables/fallbacks.py tests/unit_tests/runnables/test_fallbacks.py
  • uv run ruff check langchain_core/runnables/fallbacks.py tests/unit_tests/runnables/test_fallbacks.py
  • uv run pytest tests/unit_tests/runnables/test_fallbacks.py -q
  • repeated the same focused gate a second time
  • direct runtime repro now shows fallback child chain_start / chain_error / chain_end events carrying the root parent_run_id

Fixes #36072.

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py (modified, +88/-1)

PR #36189: fix(core): pass child_config in RunnableWithFallbacks.invoke/ainvoke to preserve parent_run_id

Description (problem / solution / changelog)

Summary

Fixes #36072 — regression from #25550.

RunnableWithFallbacks.invoke() and ainvoke() were calling child runnables with the parent config object instead of child_config. child_config is produced by patch_config(config, callbacks=run_manager.get_child()) and carries the child callback manager with the correct parent_run_id. By passing config instead, child runnables received a fresh callback manager with no parent context, causing all child callback events (on_llm_start, on_llm_error, on_llm_end, etc.) to have parent_run_id=None.

Root cause (two lines, both fixed):

  • invoke() line 196: runnable.invoke(input, config, ...)runnable.invoke(input, child_config, ...)
  • ainvoke() line 247: context.run(runnable.ainvoke, input, config, ...)context.run(runnable.ainvoke, input, child_config, ...)

stream() and astream() were already correct (stream omits config relying on context-var propagation; astream already passed child_config).

Test plan

  • Added test_invoke_child_callbacks_have_correct_parent_run_id — verifies all child LLM callback events have the root RunnableWithFallbacks run as their parent_run_id after invoke()
  • Added test_ainvoke_child_callbacks_have_correct_parent_run_id — same check for ainvoke()
  • All 18 tests in test_fallbacks.py pass

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +2/-2)
  • libs/core/tests/unit_tests/runnables/test_fallbacks.py (modified, +135/-2)

PR #36295: fix: pass child_config instead of config in RunnableWithFallbacks.invoke

Description (problem / solution / changelog)

Fixes #36072

RunnableWithFallbacks.invoke() was passing config instead of child_config to child runnables, causing child callback events to receive parent_run_id=None. This breaks tracing tools that rely on parent_run_id to build span trees.

Verified by running the reproduction script from the issue: AssertionError before the fix, clean run after.

Changed files

  • libs/core/langchain_core/runnables/fallbacks.py (modified, +1/-1)

Code Example

from langchain_openai import ChatOpenAI                                                                                            
from langchain_core.callbacks.base import BaseCallbackHandler 
from langchain_core.messages import HumanMessage                                                                                   
                                                                                
                                                                                                                                    
class Tracker(BaseCallbackHandler):                                                                                                
    def __init__(self):
        self.events = []

    def on_chain_start(self, serialized, inputs, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("chain_start", run_id, parent_run_id))

    def on_chat_model_start(self, serialized, messages, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("chat_model_start", run_id, parent_run_id))

    def on_llm_error(self, error, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("llm_error", run_id, parent_run_id))


tracker = Tracker()
llm = ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad").with_fallbacks(
    [ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad-2")]
)

try:
    llm.invoke([HumanMessage(content="hi")], config={"callbacks": [tracker]})
except Exception:
    pass

chain_run_id = tracker.events[0][1]  # run_id from chain_start
for event_name, run_id, parent_run_id in tracker.events:
    if event_name in ("chat_model_start", "llm_error"):
        assert parent_run_id == chain_run_id, (
            f"{event_name}: expected parent_run_id={chain_run_id}, got {parent_run_id}"
        )

---



---

child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:
                    output = context.run(
                        runnable.invoke,
                        input,
-                        config,
+                        child_config,
                        **kwargs,
                    )
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

#25337 — Original bug report (callbacks not firing in fallbacks), closed by #25550
#25550 — PR that introduced this regression (passed config instead of child_config)

Reproduction Steps / Example Code (Python)

from langchain_openai import ChatOpenAI                                                                                            
from langchain_core.callbacks.base import BaseCallbackHandler 
from langchain_core.messages import HumanMessage                                                                                   
                                                                                
                                                                                                                                    
class Tracker(BaseCallbackHandler):                                                                                                
    def __init__(self):
        self.events = []

    def on_chain_start(self, serialized, inputs, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("chain_start", run_id, parent_run_id))

    def on_chat_model_start(self, serialized, messages, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("chat_model_start", run_id, parent_run_id))

    def on_llm_error(self, error, *, run_id, parent_run_id=None, **kwargs):
        self.events.append(("llm_error", run_id, parent_run_id))


tracker = Tracker()
llm = ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad").with_fallbacks(
    [ChatOpenAI(model="gpt-4o-mini", api_key="sk-bad-2")]
)

try:
    llm.invoke([HumanMessage(content="hi")], config={"callbacks": [tracker]})
except Exception:
    pass

chain_run_id = tracker.events[0][1]  # run_id from chain_start
for event_name, run_id, parent_run_id in tracker.events:
    if event_name in ("chat_model_start", "llm_error"):
        assert parent_run_id == chain_run_id, (
            f"{event_name}: expected parent_run_id={chain_run_id}, got {parent_run_id}"
        )

Error Message and Stack Trace (if applicable)

Description

RunnableWithFallbacks.invoke() passes config instead of child_config to child runnables (line 196 of fallbacks.py). This causes child callback events (on_chat_model_start, on_llm_error, on_llm_end) to receive parent_run_id=None instead of the fallback chain's run_id.

This breaks any tracing/observability tool that relies on parent_run_id to build span trees — child LLM spans become orphans invisible to the trace.

Regression introduced by #25550. Fix is one line — change config to child_config:

                child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:
                    output = context.run(
                        runnable.invoke,
                        input,
-                        config,
+                        child_config,
                        **kwargs,
                    )

Same issue likely applies to ainvoke, batch, and abatch.

System Info

from langchain_core import sys_info sys_info.print_sys_info()

System Information

OS: Darwin OS Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:56:35 PST 2026; root:xnu-12377.91.3~2/RELEASE_ARM64_T6030 Python Version: 3.12.8 (main, Dec 6 2024, 19:42:06) [Clang 18.1.8 ]

Package Information

langchain_core: 1.2.19 langchain: 1.2.10 langsmith: 0.7.9 langchain_google_vertexai: 3.2.2 langchain_openai: 1.1.10 langchain_tests: 1.1.5 langgraph_sdk: 0.3.9

Optional packages not installed

deepagents deepagents-cli

Other Dependencies

bottleneck: 1.6.0 google-cloud-aiplatform: 1.139.0 google-cloud-storage: 3.9.0 google-cloud-vectorsearch: 0.5.0 httpx: 0.28.1 httpx-sse: 0.4.3 jsonpatch: 1.33 langgraph: 1.0.10 numexpr: 2.14.1 numpy: 2.4.2 openai: 2.29.0 openai-agents: 0.12.4 opentelemetry-api: 1.39.1 opentelemetry-exporter-otlp-proto-http: 1.39.1 opentelemetry-sdk: 1.39.1 orjson: 3.11.7 packaging: 26.0 pyarrow: 22.0.0 pydantic: 2.12.5 pytest: 9.0.2 pytest-asyncio: 1.3.0 pytest-benchmark: 5.2.3 pytest-codspeed: 4.3.0 pytest-recording: 0.13.4 pytest-socket: 0.7.0 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 rich: 14.3.3 syrupy: 5.1.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 validators: 0.35.0 vcrpy: 8.1.1 websockets: 16.0 wrapt: 1.17.3 xxhash: 3.6.0 zstandard: 0.25.0

extent analysis

Fix Plan

To fix the issue, we need to modify the invoke method in the fallbacks.py file to pass child_config instead of config to the child runnables.

Here are the steps:

  • Open the fallbacks.py file and navigate to the invoke method.
  • Replace the line output = context.run(runnable.invoke, input, config, **kwargs) with output = context.run(runnable.invoke, input, child_config, **kwargs).
  • The corrected code should look like this:
child_config = patch_config(config, callbacks=run_manager.get_child())
with set_config_context(child_config) as context:
    output = context.run(
        runnable.invoke,
        input,
        child_config,  # Changed from config to child_config
        **kwargs,
    )

This change will ensure that the child callback events receive the correct parent_run_id, fixing the issue with tracing and observability tools.

Verification

To verify that the fix worked, you can run the provided example code again and check that the parent_run_id is correctly set for the child callback events. You can do this by adding print statements or using a debugger to inspect the tracker.events list.

Extra Tips

  • Make sure to update the fallbacks.py file in the correct location, depending on your project structure and dependencies.
  • If you're using a version control system, consider creating a new branch or commit to track the changes.
  • After applying the fix, re-run any tests or verification scripts to ensure that the issue is fully resolved.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING