BUG introduced 3 days ago: callbackManager.copy is not a function #102

Closed
opened 2026-02-15 17:15:46 -05:00 by yindo · 3 comments
Owner

Originally created by @max-sym on GitHub (Sep 29, 2024).

A bug introduced 3 days ago by @jacoblee93 here

When I try calling ToolNode manually via toolNode.invoke(...) it gives me the following error:

TypeError: callbackManager.copy is not a function
    at Function._configureSync (callbacks/manager.cjs:736:47)
    at getCallbackManagerForConfig (runnables/config.cjs:8:41)
    at ToolNode._callWithConfig (runnables/base.cjs:218:84)
    at ToolNode.invoke (langgraph/utils.cjs:79:38)
    at AgentCRAG.callTools (chat/agent.ts:262:36)
    at (runnables/base.cjs:1531:45)
    at AsyncLocalStorage.run (async_hooks:91:14)
    at AsyncLocalStorageProvider.runWithConfig (singletons/index.cjs:48:24)
    at (runnables/base.cjs:1529:64)
    at RunnableLambda._invoke (runnables/base.cjs:1524:16)
    at RunnableLambda._callWithConfig (runnables/base.cjs:223:34)
    at processTicksAndRejections (task_queues:105:5)
    at RunnableSequence.invoke (runnables/base.cjs:1155:33)

Let's go step by step

If we go to _configureSync (file link), what do we see?

static _configureSync(
// ...
  ) {
    let callbackManager: CallbackManager | undefined;
    if (inheritableHandlers || localHandlers) {
      if (Array.isArray(inheritableHandlers) || !inheritableHandlers) {
        callbackManager = new CallbackManager();
        callbackManager.setHandlers(
          inheritableHandlers?.map(ensureHandler) ?? [],
          true
        );
      } else {
        callbackManager = inheritableHandlers;
      }
      callbackManager = callbackManager.copy( // 👈 ERROR: callbackManager.copy is not a function
        Array.isArray(localHandlers)
          ? localHandlers.map(ensureHandler)
          : localHandlers?.handlers,
        false
      );
    }

When I log callbackManager right before callbackManager.copy(...), it's shown as an object in the case of ToolNode but as a class in other cases:

console.log("callbackManager", { callbackManager, copy: callbackManager.copy })

This outputs:


// ----- OTHER CASES

callbackManager {callbackManager: CallbackManager {
  //...
}, copy: [Function]} // 👈 Notice that it's defined and is a Function

// ----- CASE OF ToolNode

callbackManager {callbackManager: {
  //...
}, copy: undefined} // 👈 undefined !!!

TLDR: Going down the rabbit hole this is the cause:

import { RunnableConfig } from "@langchain/core/runnables";
import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
import { LangGraphRunnableConfig } from "../runnable_types.js";

const COPIABLE_KEYS = ["tags", "metadata", "callbacks", "configurable"];  // 👈 Callbacks is in COPIABLE_KEYS

// ...

export function ensureLangGraphConfig(
  ...configs: (LangGraphRunnableConfig | undefined)[]
): RunnableConfig {

  //...

  const implicitConfig: RunnableConfig =
    AsyncLocalStorageProviderSingleton.getRunnableConfig();
  if (implicitConfig !== undefined) {
    for (const [k, v] of Object.entries(implicitConfig)) {
      if (v !== undefined) {
        if (COPIABLE_KEYS.includes(k)) {
          let copiedValue;
          if (Array.isArray(v)) {
            copiedValue = [...v];
          } else if (typeof v === "object") {
            copiedValue = { ...v }; // 👈 this would copy CallbackManager as an object instead of using v.copy() and creating another instance of a class!!!
          } else {
            copiedValue = v;
          }
          empty[k as keyof RunnableConfig] = copiedValue;
        } else {
          empty[k as keyof RunnableConfig] = v;
        }
      }
    }
  }
//...
}
Originally created by @max-sym on GitHub (Sep 29, 2024). ### A bug introduced 3 days ago by @jacoblee93 [here](https://github.com/langchain-ai/langgraphjs/commit/7abbb4496c001b775e32acd189061f3ca32717ef#diff-dc89325a5031e83dd4bb8100fc14a3b45bf9a40a9c3b2b4a6b7403b09515f1deR75) When I try calling ToolNode manually via `toolNode.invoke(...)` it gives me the following error: ```ts TypeError: callbackManager.copy is not a function at Function._configureSync (callbacks/manager.cjs:736:47) at getCallbackManagerForConfig (runnables/config.cjs:8:41) at ToolNode._callWithConfig (runnables/base.cjs:218:84) at ToolNode.invoke (langgraph/utils.cjs:79:38) at AgentCRAG.callTools (chat/agent.ts:262:36) at (runnables/base.cjs:1531:45) at AsyncLocalStorage.run (async_hooks:91:14) at AsyncLocalStorageProvider.runWithConfig (singletons/index.cjs:48:24) at (runnables/base.cjs:1529:64) at RunnableLambda._invoke (runnables/base.cjs:1524:16) at RunnableLambda._callWithConfig (runnables/base.cjs:223:34) at processTicksAndRejections (task_queues:105:5) at RunnableSequence.invoke (runnables/base.cjs:1155:33) ``` ## Let's go step by step If we go to `_configureSync` ([file link](https://github.com/langchain-ai/langchainjs/blob/e9a6aad1fa62c1d09402284aea2d790e5e7832de/langchain-core/src/callbacks/manager.ts#L1173)), what do we see? ```ts static _configureSync( // ... ) { let callbackManager: CallbackManager | undefined; if (inheritableHandlers || localHandlers) { if (Array.isArray(inheritableHandlers) || !inheritableHandlers) { callbackManager = new CallbackManager(); callbackManager.setHandlers( inheritableHandlers?.map(ensureHandler) ?? [], true ); } else { callbackManager = inheritableHandlers; } callbackManager = callbackManager.copy( // 👈 ERROR: callbackManager.copy is not a function Array.isArray(localHandlers) ? localHandlers.map(ensureHandler) : localHandlers?.handlers, false ); } ``` When I log `callbackManager` right before `callbackManager.copy(...)`, it's shown as an object in the case of `ToolNode` but as a class in other cases: ```ts console.log("callbackManager", { callbackManager, copy: callbackManager.copy }) ``` This outputs: ``` // ----- OTHER CASES callbackManager {callbackManager: CallbackManager { //... }, copy: [Function]} // 👈 Notice that it's defined and is a Function // ----- CASE OF ToolNode callbackManager {callbackManager: { //... }, copy: undefined} // 👈 undefined !!! ``` ### TLDR: Going down the rabbit hole this is the cause: ```ts import { RunnableConfig } from "@langchain/core/runnables"; import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons"; import { LangGraphRunnableConfig } from "../runnable_types.js"; const COPIABLE_KEYS = ["tags", "metadata", "callbacks", "configurable"]; // 👈 Callbacks is in COPIABLE_KEYS // ... export function ensureLangGraphConfig( ...configs: (LangGraphRunnableConfig | undefined)[] ): RunnableConfig { //... const implicitConfig: RunnableConfig = AsyncLocalStorageProviderSingleton.getRunnableConfig(); if (implicitConfig !== undefined) { for (const [k, v] of Object.entries(implicitConfig)) { if (v !== undefined) { if (COPIABLE_KEYS.includes(k)) { let copiedValue; if (Array.isArray(v)) { copiedValue = [...v]; } else if (typeof v === "object") { copiedValue = { ...v }; // 👈 this would copy CallbackManager as an object instead of using v.copy() and creating another instance of a class!!! } else { copiedValue = v; } empty[k as keyof RunnableConfig] = copiedValue; } else { empty[k as keyof RunnableConfig] = v; } } } } //... } ```
yindo closed this issue 2026-02-15 17:15:46 -05:00
Author
Owner

@jacoblee93 commented on GitHub (Sep 30, 2024):

Hey there, sorry about this - will have a look!

@jacoblee93 commented on GitHub (Sep 30, 2024): Hey there, sorry about this - will have a look!
Author
Owner

@jacoblee93 commented on GitHub (Sep 30, 2024):

I am having a hard time reproing this - I think you are correct in that it's a mistake but would love to add a proper test case. Can you share a bit more code?

@jacoblee93 commented on GitHub (Sep 30, 2024): I am having a hard time reproing this - I think you are correct in that it's a mistake but would love to add a proper test case. Can you share a bit more code?
Author
Owner

@jacoblee93 commented on GitHub (Sep 30, 2024):

Ok, managed to repro. Will fix, thanks again for sharing

@jacoblee93 commented on GitHub (Sep 30, 2024): Ok, managed to repro. Will fix, thanks again for sharing
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#102