{"record":{"id":"e7276cffa145d393","repo":"JuliusBrussee/caveman","slug":"native-model-does-not-support-bindtools","errorCode":null,"errorMessage":"Native model does not support bindTools","messagePattern":"Native model does not support bindTools","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/middleware/typescript/src/langchain-model.ts","lineNumber":30,"sourceCode":"  if(Array.isArray(input))return input.map(coerceMessageLikeToMessage);\n  return input.toChatMessages();\n}\n\n/** Public BaseChatModel delegate; provider configuration lives on inner only. */\nexport class CavemanChatModel<Options extends BaseChatModelCallOptions=BaseChatModelCallOptions> extends BaseChatModel<Options>{\n  override withStructuredOutput:BaseChatModel<Options>['withStructuredOutput'];\n  constructor(readonly inner:BaseChatModel<Options>,readonly caveman:LangChainOptions){\n    super({});\n    this.withStructuredOutput=((...args:Parameters<BaseChatModel<Options>['withStructuredOutput']>)=>this.project().pipe(inner.withStructuredOutput(...args))) as BaseChatModel<Options>['withStructuredOutput'];\n  }\n  _llmType():string{return this.inner?this.inner._llmType():'caveman';}\n  private project(){return RunnableLambda.from<BaseLanguageModelInput,BaseMessage[],Options>(async(input,config)=>{\n    const prepared=await prepareLangChain(messages(input),this.caveman,config);\n    if(prepared.attempt){const attempt=prepared.attempt;attempt.runtime.report(attempt.optimization,{reason:attempt.reason??'no_candidate',adapter:'langchain',logicalCallId:attempt.logicalCallId,attemptId:attempt.attemptId});}\n    return prepared.messages;\n  });}\n  override bindTools(tools:BindToolsInput[],kwargs?:Partial<Options>){\n    if(!this.inner.bindTools)throw new Error('Native model does not support bindTools');\n    return this.project().pipe(this.inner.bindTools(tools,kwargs)) as ReturnType<NonNullable<BaseChatModel<Options>['bindTools']>>;\n  }\n  override async invoke(input:BaseLanguageModelInput,config?:Partial<Options>):Promise<AIMessageChunk>{\n    const prepared=await prepareLangChain(messages(input),this.caveman,config as RunnableConfig|undefined);\n    if(!prepared.attempt)return this.inner.invoke(input,config);\n    const attempt=prepared.attempt;observe(attempt,'dispatch_intent');\n    try{const response=await withOwner(attempt,()=>this.inner.invoke(prepared.messages,config));observe(attempt,'completed',isAIMessage(response)?langChainUsage(response.usage_metadata):null);return response;}\n    catch(error){observe(attempt,config?.signal?.aborted?'cancelled':'failed');throw error;}\n  }\n  override async stream(input:BaseLanguageModelInput,config?:Partial<Options>):Promise<IterableReadableStream<AIMessageChunk>>{\n    const prepared=await prepareLangChain(messages(input),this.caveman,config as RunnableConfig|undefined);\n    if(!prepared.attempt)return this.inner.stream(input,config);\n    const attempt=prepared.attempt;observe(attempt,'dispatch_intent');\n    const stream=await withOwner(attempt,()=>this.inner.stream(prepared.messages,config));\n    async function* values(){\n      const iterator=stream[Symbol.asyncIterator]();let last,finished=false;\n      try{for(;;){const next=await withOwner(attempt,()=>iterator.next());if(next.done){finished=true;observe(attempt,'completed',langChainUsage(last));return;}if(isAIMessageChunk(next.value)&&next.value.usage_metadata)last=next.value.usage_metadata;yield next.value;}}\n      catch(error){observe(attempt,config?.signal?.aborted?'cancelled':'failed');finished=true;throw error;}","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/typescript/src/langchain-model.ts#L12-L48","documentation":"CavemanLangChainModel.bindTools delegates to the wrapped native model's bindTools. If the inner BaseChatModel does not implement bindTools (it is undefined), the library throws rather than silently dropping tool-calling. The middleware cannot project a tool-bound runnable without native support.","triggerScenarios":"Calling cavemanModel.bindTools(tools) where this.inner.bindTools is falsy — i.e. the wrapped chat model class has no bindTools implementation.","commonSituations":"Wrapping a model that predates LangChain.js tool-calling support, using a community/custom model that never implemented bindTools, or a LangChain version mismatch where the inner model lacks the API.","solutions":["Swap the wrapped model for one that supports tool calling (e.g. ChatOpenAI, ChatAnthropic).","Upgrade @langchain/core and the model package so the inner model implements bindTools.","If tools are unnecessary for that model, call invoke directly without bindTools.","Guard with a capability check before binding."],"exampleFix":"// before\nconst model = new CavemanLangChainModel(new SomeLegacyModel());\nmodel.bindTools([myTool]); // throws\n// after\nif (new SomeLegacyModel().bindTools) {\n  new CavemanLangChainModel(new SomeLegacyModel()).bindTools([myTool]);\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function supportsBindTools(m: BaseChatModel): m is BaseChatModel & { bindTools: NonNullable<BaseChatModel['bindTools']> } {\n  return typeof m.bindTools === 'function';\n}","tryCatchPattern":"try {\n  return cavemanModel.bindTools(tools, kwargs);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Native model does not support bindTools') {\n    // fall back to a model with native tool support, or run unbound\n    return plainToolCapableModel.bindTools(tools, kwargs);\n  } else throw e;\n}","preventionTips":["Check innerModel.bindTools before wrapping with CavemanLangChainModel","Prefer officially supported tool-calling models (ChatOpenAI, ChatAnthropic)","Keep @langchain/core current so model classes implement bindTools","Add a unit test asserting tool binding works for each model you wrap"],"tags":["langchain","tool-calling","unsupported-operation","typescript"],"backgroundTag":"unsupported-operation","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}