LangChain (YT style)LangChain 1.4 · Python 3.12+
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

Before and after hooks, then a layered stack · from the Guardrails with LangChain: A Complete Crash Course · 35:15 to 37:35

Layers run in list order

The clip starts where the guardrails crash course moves from before_agent to after_agent. A before-agent hook is an input filter: it runs as soon as the input arrives. An after-agent hook runs once the agent has produced its output, which suits model-based safety evaluation, compliance scanning and removing sensitive information that slipped through; the video's example asks a second model whether each reply is safe. Then the crash course stacks the guardrails it built into one agent, one by one: a content filter as layer 1, PIIMiddleware as layer 2, human-in-the-loop approval next, PII redaction on the output, and a model-based output safety check last.

That list order is the order the checks meet a request. Back to the airport from Middleware: code around the model: you pass the bag check before immigration, never the other way round. The first entry in the list is the outer layer: its before hook runs first and its after hook runs last. When two middleware touch the same thing, the order decides which one sees the other's change. A small class that prints its own name makes the order visible.

The video shows ordering with its stack of guardrails. The code below makes the same rule visible with a smaller example: a middleware class that prints its name from each hook, so the order is printed as it happens.

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. The name property matters: LangChain names each middleware after its class unless told otherwise, and refuses two with the same name.

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

Add the two hooks to the class, 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)

Running two middleware in order

Create the agent with two tags, one labelled first and one second, on the same Groq model as Middleware: code around the model; no tools are needed to say hello. Before hooks fire first to last, after hooks last to first. The reply itself is not printed; only the hooks are.

ExampleAPI key
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0)
agent = create_agent(model, 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. Here is that mistake, so you recognise the error when you meet it.

ExampleAPI key
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware
from langchain.chat_models import init_chat_model


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


model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0)
agent = create_agent(model, 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.

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. Name the hook's argument runtime, or it fails on the first question, not when the agent is built.
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.