Skip to content

AsyncCallbackManager.on_llm_start drops non-inline handlers when an inline handler is registered #39633

Description

@Shailendra005

Submission checklist

  • 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

No response

Reproduction Steps / Example Code (Python)

import asyncio
from langchain_core.callbacks import AsyncCallbackManager, AsyncCallbackHandler

received = []

class H(AsyncCallbackHandler):
    def __init__(self, name, run_inline):
        self.name = name
        self.run_inline = run_inline
    async def on_llm_start(self, *args, **kwargs):
        received.append(self.name)

mgr = AsyncCallbackManager(handlers=[H("inline", True), H("non_inline", False)])
asyncio.run(mgr.on_llm_start({}, ["hi"]))
print(received)  # got: ['inline']   expected: ['inline', 'non_inline']

Error Message and Stack Trace (if applicable)

Description

AsyncCallbackManager.on_llm_start dispatches to non-inline handlers only when there are zero inline handlers.

Location: libs/core/langchain_core/callbacks/manager.py (the if inline_handlers: ... else: non_inline_tasks... block in on_llm_start). Because the else runs only when no inline handler exists, registering any inline handler suppresses on_llm_start for every non-inline handler.

Real-world trigger: LangChainTracer (the LangSmith tracer) sets run_inline = True. So in any async app with tracing enabled, a custom AsyncCallbackHandler (non-inline by default) never receives on_llm_start for completion-style LLMs, yet still receives on_llm_end (dispatched to all handlers). Handlers that track state per run get an end with no start.

The sibling on_chat_model_start is correct: it loops over self.handlers, builds a per-handler task, and classifies each by handler.run_inline. on_llm_start should mirror that.

Disclosure: this was found and fixed with AI assistance. I have a branch with the fix (mirroring on_chat_model_start) plus a regression test that fails before and passes after, and would like to be assigned before opening the PR.

System Info

langchain-core current master / latest. Python 3.13.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugRelated to a bug, vulnerability, unexpected error with an existing featurecore`langchain-core` package issues & PRsexternal

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions