LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

The order middleware runs in

Middleware order is the sequence the hooks run in: before hooks run in list order and after hooks run in reverse.

Last updated: 27 Sep, 2026 · LangChain 1.4

Get the order wrong and a hook sees the state before the change you expected. A class gives middleware a name and room for more than one hook, so two of them can show the order.

The order middleware runs in
middleware=[first, second]What runs, top to bottomfirstoutermost layersecondinside the firstfirst.before_modelsecond.before_modelthe model callsecond.after_modelfirst.after_modeltools, then round again
Hover or tap a piece to see what it is and which lesson built it.
Follow the order

Pick one to watch it run, step by step.

The AgentMiddleware class

python
from langchain.agents.middleware import AgentMiddleware

class Tag(AgentMiddleware):
    def before_model(self, state, runtime): ...  # runs first to last
    def after_model(self, state, runtime): ...   # runs last to first

agent = create_agent(model, tools=[], middleware=[Tag(), Tag()])

The Tag middleware class

Write a middleware class. It takes a label and gives itself a name from it.

python
from langchain.agents.middleware import AgentMiddleware

class Tag(AgentMiddleware):
    def __init__(self, label):
        super().__init__()
        self.label = label

    @property
    def name(self):          # each middleware needs a unique name
        return self.label

The before and after methods

The name property matters: LangChain names each middleware after its class unless told otherwise, and refuses two with the same name. Add the two hooks, each printing the label.

python
    def before_model(self, state, runtime):
        print("before", self.label)

    def after_model(self, state, runtime):
        print("after ", self.label)

The agent with two middleware

Create the agent with two tags, one labelled first and one second.

python
from langchain.agents import create_agent
from shop_model import ShopModel
from tag import Tag

agent = create_agent(ShopModel(), tools=[], middleware=[Tag("first"), Tag("second")])

Running two middleware in order

Run it. Before hooks fire first to last, after hooks last to first.

Example
from langchain.agents import create_agent
from shop_model import ShopModel
from tag import Tag

agent = create_agent(ShopModel(), tools=[], middleware=[Tag("first"), Tag("second")])
agent.invoke({"messages": [{"role": "user", "content": "Hello"}]})

The argument has to be called runtime

LangChain passes the runtime to a hook by the name runtime. A hook that calls the argument rt fails the first time it runs. Cause the error before the fix.

Example
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware
from shop_model import ShopModel


class Short(AgentMiddleware):
    def before_model(self, state, rt):
        print(len(state["messages"]))


agent = create_agent(ShopModel(), tools=[], middleware=[Short()])
agent.invoke({"messages": [{"role": "user", "content": "Hello"}]})

What the print order shows

  • Before hooks ran first to last, in the order of the list; after hooks ran last to first. Picture the list as layers around the model: the first entry is the outermost, first in and last out.
  • The name property gives each middleware a unique name; LangChain refuses two with the same name, so the tags differ by label.
  • The hook argument has to be called runtime. A hook that calls it rt raises TypeError the first time it runs, and the documentation does not mention this.

Before hooks vs after hooks

before hooksafter hooks
OrderList order, first to lastReverse, last to first
LayerFirst entry is outermost, first inFirst entry is outermost, last out
SeesThe state before the modelThe state after the model

Where ordering matters

  • Stacking middleware where an outer one must wrap an inner one, like a guard around a logger.
  • Working out which middleware sees a change first when two touch the same state.
Watch out. The hook argument must be named runtime. Call it anything else and the hook raises TypeError the first time it runs, with a message about a missing positional argument.
Try it yourself
  • Pass Tag("first") twice and read the error.
  • Add a third Tag in the middle of the list and predict the six lines before you run it.
  • Add a before_agent method to Tag and see where it prints.

Every expert started right here.