{"record":{"id":"e077e7a835fbb48c","repo":"langchain-ai/langchain","slug":"unable-to-dispatch-an-adhoc-event-without-a-parent","errorCode":null,"errorMessage":"Unable to dispatch an adhoc event without a parent run id.This function can only be called from within an existing run (e.g.,inside a tool or a RunnableLambda or a RunnableGenerator.)If you are doing that and still seeing this error, try explicitlypassing the config parameter to this function.","messagePattern":"Unable to dispatch an adhoc event without a parent run id\\.This function can only be called from within an existing run \\(e\\.g\\.,inside a tool or a RunnableLambda or a RunnableGenerator\\.\\)If you are doing that and still seeing this error, try explicitlypassing the config parameter to this function\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/callbacks/manager.py","lineNumber":2730,"sourceCode":"        ensure_config,\n        get_async_callback_manager_for_config,\n    )\n\n    config = ensure_config(config)\n    callback_manager = get_async_callback_manager_for_config(config)\n    # We want to get the callback manager for the parent run.\n    # This is a work-around for now to be able to dispatch adhoc events from\n    # within a tool or a lambda and have the metadata events associated\n    # with the parent run rather than have a new run id generated for each.\n    if callback_manager.parent_run_id is None:\n        msg = (\n            \"Unable to dispatch an adhoc event without a parent run id.\"\n            \"This function can only be called from within an existing run (e.g.,\"\n            \"inside a tool or a RunnableLambda or a RunnableGenerator.)\"\n            \"If you are doing that and still seeing this error, try explicitly\"\n            \"passing the config parameter to this function.\"\n        )\n        raise RuntimeError(msg)\n\n    await callback_manager.on_custom_event(\n        name,\n        data,\n        run_id=callback_manager.parent_run_id,\n    )\n\n\ndef dispatch_custom_event(\n    name: str, data: Any, *, config: RunnableConfig | None = None\n) -> None:\n    \"\"\"Dispatch an adhoc event.\n\n    Args:\n        name: The name of the adhoc event.\n        data: The data for the adhoc event.\n\n            Free form data. Ideally should be JSON serializable to avoid serialization","sourceCodeStart":2712,"sourceCodeEnd":2748,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/callbacks/manager.py#L2712-L2748","documentation":"`adispatch_custom_event` (manager.py ~2730) needs a parent run to attach the event to; it looks up the current run's callback manager and raises `RuntimeError` when `parent_run_id is None`. Without an active run (or without passing the `config` of the current run), there is no run to parent the event to.","triggerScenarios":"Calling `await adispatch_custom_event(...)` outside any runnable execution; calling it inside a tool/lambda but omitting the `config` parameter so the contextvar lookup finds no run; dispatching from a thread the contextvars didn't propagate to.","commonSituations":"Custom events fired from helper functions that never received `config`; async tools that drop `run_manager`/config; events dispatched in background tasks spawned off the run's task tree; calling the API at module import or in tests without a run context.","solutions":["Pass the config explicitly: `await adispatch_custom_event(name, data, config=config)` inside the tool/lambda that received it","Only dispatch from within a runnable, tool, RunnableLambda, or RunnableGenerator execution","If outside a run, wrap the logic in a `RunnableLambda` (or start a run via a tracer) so a parent exists","Ensure contextvars propagate into spawned tasks (create tasks from within the run, not before it)"],"exampleFix":"# before\nasync def my_tool(args: dict) -> str:\n    await adispatch_custom_event(\"tool_note\", {\"stage\": \"start\"})\n    ...\n\n# after\nasync def my_tool(args: dict, config: RunnableConfig) -> str:\n    await adispatch_custom_event(\"tool_note\", {\"stage\": \"start\"}, config=config)\n    ...","handlingStrategy":"try-catch","validationCode":"from langchain_core.callbacks import adispatch_custom_event\n\ndef has_parent_run(config) -> bool:\n    return bool(config and config.get('callbacks'))  # heuristic; prefer structural check\n\n# structural prevention: always pass config from the enclosing runnable","typeGuard":null,"tryCatchPattern":"try:\n    await adispatch_custom_event(name, data, config=config)\nexcept RuntimeError as e:\n    if 'parent run id' in str(e):\n        logger.debug('no active run for %s; skipping event', name)\n    else:\n        raise","preventionTips":["Always forward config into helpers that dispatch events","Dispatch only from inside tools/RunnableLambda/RunnableGenerator","Spawn background tasks from within the run so contextvars propagate"],"tags":["callbacks","events","async","runtime-context"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}