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.
Pick one to watch it run, step by step.
The AgentMiddleware class
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.
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.labelThe 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.
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.
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.
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.
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 itrtraisesTypeErrorthe first time it runs, and the documentation does not mention this.
Before hooks vs after hooks
| before hooks | after hooks | |
|---|---|---|
| Order | List order, first to last | Reverse, last to first |
| Layer | First entry is outermost, first in | First entry is outermost, last out |
| Sees | The state before the model | The 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.
runtime. Call it anything else and the hook raises TypeError the first time it runs, with a message about a missing positional argument.Related
- Previous: Middleware: code around the model
- Next: Changing the call: wrap_model_call
- Reference: Middleware
- Pass
Tag("first")twice and read the error. - Add a third
Tagin the middle of the list and predict the six lines before you run it. - Add a
before_agentmethod toTagand see where it prints.
Every expert started right here.