1

I have a LangGraph agent that is supposed to interact with the user in multi-round conversations and perform modifications to a kubernetes environment. I am using an external Model Context Protocol server to provide the tools to interact with the k8s configuration and for now my agent construction is simply the LangChain create_agent function.
Now, the agent could use the tools incorrectly (e.g. attempting to delete a non-existant k8s pod) and the tool would raise a ToolException whose description could aid the model in refining its approach. Since the state of the agent requires a ToolMessage corresponding to each AIMessage that corresponds to a tool call I want to catch the exception and append its description as the response ToolMessage in the state of the agent. However, I am getting a hard-to-debug error that suggests I am going about this incorrectly.

Here is my agent code:

from langchain.agents import create_agent
from langchain_core.messages import BaseMessage
from langchain.messages import ToolMessage, AIMessage
from langgraph.graph import MessagesState
from langgraph.checkpoint.memory import InMemorySaver
from langchain.tools import ToolException

class MyAgent:
    def __init__(self):
        model = ...
        tools = []

        self.config = {...}

        self.agent = create_agent(
            model,
            tools=tools,
            state_schema=MessagesState,
            checkpointer=InMemorySaver()
        )

    def run(self,cmd:dict) -> BaseMessage:
        try:
            result = self.agent.invoke(cmd,self.config)
            return result["messages"][-1]
        except ToolException as expt:
            
            # ERROR HANDLING
            # get state snapshot to find out tool call id
            snapshot = self.agent.get_state(self.config)
            last_tool_call = snapshot.values["messages"][-1].tool_calls[0]
            # compose toolmessage to signal the error
            error_tool_message = ToolMessage(
                content=f"Couldn't execute tool because of: {expt}",
                tool_call_id=last_tool_call["id"]
            )

            # ERROR COMES FROM HERE
            # update the state
            self.agent.update_state(
                self.config,
                {"messages":[error_tool_message]}
            )

            # return error notification to the user
            return AIMessage(
                f"Couldn't execute because of tool error: {expt}"
            )

When I try to run the agent with a message that asks it to delete a non-existent pod I get the following error message:

Traceback (most recent call last):
  File "/home/ubuntu/dev/elster/src/elster/agent.py", line 144, in run
    result = self.event_loop.run_until_complete(
        self.agent.ainvoke(cmd,config=self.config)
    )
  File "/home/ubuntu/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/main.py", line 3182, in ainvoke
    async for chunk in self.astream(
    ...<29 lines>...
            chunks.append(chunk)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/main.py", line 3000, in astream
    async for _ in runner.atick(
    ...<13 lines>...
            yield o
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/_runner.py", line 304, in atick
    await arun_with_retry(
    ...<15 lines>...
    )
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/_retry.py", line 137, in arun_with_retry
    return await task.proc.ainvoke(task.input, config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/_internal/_runnable.py", line 705, in ainvoke
    input = await asyncio.create_task(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
        step.ainvoke(input, config, **kwargs), context=context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/_internal/_runnable.py", line 473, in ainvoke
    ret = await self.afunc(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 733, in _afunc
    outputs = await asyncio.gather(*coros)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 1066, in _arun_one
    return await self._execute_tool_async(tool_request, input_type, config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 1015, in _execute_tool_async
    content = _handle_tool_error(e, flag=self._handle_tool_errors)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 389, in _handle_tool_error
    content = flag(e)  # type: ignore [assignment, call-arg]
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 352, in _default_handle_tool_errors
    raise e
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain/tools/tool_node.py", line 970, in _execute_tool_async
    response = await tool.ainvoke(call_args, config)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/tools/structured.py", line 63, in ainvoke
    return await super().ainvoke(input, config, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/tools/base.py", line 601, in ainvoke
    return await self.arun(tool_input, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/tools/base.py", line 969, in arun
    raise error_to_raise
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/tools/base.py", line 938, in arun
    response = await coro_with_context(coro, context)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/tools/structured.py", line 117, in _arun
    return await self.coroutine(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_mcp_adapters/tools.py", line 292, in call_tool
    return _convert_call_tool_result(call_tool_result)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_mcp_adapters/tools.py", line 80, in _convert_call_tool_result
    raise ToolException(tool_content)
langchain_core.tools.base.ToolException: failed to delete pod aaa in namespace : pods "aaa" not found
During task with name 'tools' and id '8fb89a71-87c5-ea74-9154-9fbc2b3b2503'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ubuntu/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/runpy.py", line 198, in _run_module_as_main
    return _run_code(code, main_globals, None,
                     "__main__", mod_spec)
  File "/home/ubuntu/.local/share/uv/python/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/runpy.py", line 88, in _run_code
    exec(code, run_globals)
    ~~~~^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 71, in <module>
    cli.main()
    ~~~~~~~~^^
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 508, in main
    run()
    ~~~^^
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 358, in run_file
    runpy.run_path(target, run_name="__main__")
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 310, in run_path
    return _run_module_code(code, init_globals, run_name, pkg_name=pkg_name, script_name=fname)
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 127, in _run_module_code
    _run_code(code, mod_globals, init_globals, mod_name, mod_spec, pkg_name, script_name)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/.vscode-server/extensions/ms-python.debugpy-2025.14.1-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 118, in _run_code
    exec(code, run_globals)
    ~~~~^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/src/elster/main.py", line 104, in <module>
    main()
    ~~~~^^
  File "/home/ubuntu/dev/elster/src/elster/main.py", line 97, in main
    msg = agent.run(cmd)["messages"][-1]
          ~~~~~~~~~^^^^^
  File "/home/ubuntu/dev/elster/src/elster/agent.py", line 155, in run
    self.agent.update_state(self.config,{"messages":[error_tool_message]})
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/main.py", line 2359, in update_state
    return self.bulk_update_state(config, [[StateUpdate(values, as_node, task_id)]])
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/main.py", line 1883, in bulk_update_state
    current_config = perform_superstep(current_config, superstep)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/pregel/main.py", line 1818, in perform_superstep
    run.invoke(
    ~~~~~~~~~~^
        values,
        ^^^^^^^
    ...<23 lines>...
        ),
        ^^
    )
    ^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langchain_core/runnables/base.py", line 3090, in invoke
    input_ = context.run(step.invoke, input_, config)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/_internal/_runnable.py", line 400, in invoke
    ret = self.func(*args, **kwargs)
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/graph/_branch.py", line 167, in _route
    return self._finish(writer, input, result, config)
           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/dev/elster/.venv/lib/python3.13/site-packages/langgraph/graph/_branch.py", line 203, in _finish
    r if isinstance(r, Send) else self.ends[r] for r in result
                                  ~~~~~~~~~^^^
KeyError: 'model'

What am I doing incorrectly here?

1 Answer 1

2
from langchain.agents import create_agent
from langchain_core.messages import AIMessage, ToolMessage, HumanMessage
from langchain_core.tools import tool, ToolException
from langgraph.graph import MessagesState
from langgraph.checkpoint.memory import InMemorySaver
from langchain_openai import ChatOpenAI


@tool
def delete_pod(pod_name: str) -> str:
    if pod_name == "nonexistent":
        raise ToolException(f"Pod '{pod_name}' not found")
    return f"Pod '{pod_name}' deleted successfully"



def make_agent():
    model = some model
    tools = [delete_pod]
    checkpointer = InMemorySaver()

    agent = create_agent(
        model=model.get_client(),
        tools=tools,
        state_schema=MessagesState,
        checkpointer=checkpointer,
    )
    return agent



class MyAgent:
    def __init__(self):
        self.agent = make_agent()
        self.config = {"configurable": {"thread_id": "abc123"}}

    def run(self, cmd):
        try:
            # Run normally
            result = self.agent.invoke(cmd, self.config)
            return result["messages"][-1]

        except ToolException as expt:
           
            snapshot = self.agent.get_state(self.config)
            last_tool_call = snapshot.values["messages"][-1].tool_calls[0]


            error_tool_message = ToolMessage(
                content=f"Couldn't execute tool because of: {expt}",
                tool_call_id=last_tool_call["id"],
            )

            # update state properly: specify as_node="tools"
            self.agent.update_state(
                self.config,
                {"messages": [error_tool_message]},
                as_node="tools"
            )

            return AIMessage(content=f"Tool failed with error: {expt}")



if __name__ == "__main__":
    agent = MyAgent()

    print(">>> Trying with valid pod")
    msg = agent.run({"messages": [HumanMessage(content="Delete pod testpod")]})
    print(msg.content)

    print("\n>>> Trying with non-existent pod")
    msg = agent.run({"messages": [HumanMessage(content="Delete pod nonexistent")]})
    print(msg.content)

You are missing as_node in the self.agent.update_state() try updating the as_node and it should work fine.

Above I have attached a working code.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.