Guarding the desk's input and output
An input guardrail is a check that runs before the model and stops the run when its tripwire fires, so a message with a password never reaches the desk.
Last updated: 28 Sep, 2026 · openai-agents 0.22.3
The desk from The desk agent and its tools answers whatever it is asked. A guardrail, from Input guardrails and the tripwire, sits in front of it and refuses input you do not want the model to see.
The pieces, and where each came from
| Piece | From lesson |
|---|---|
| The desk with its lookup tool | The desk agent and its tools |
| @input_guardrail returning GuardrailFunctionOutput | Input guardrails and the tripwire |
| InputGuardrailTripwireTriggered on a block | Input guardrails and the tripwire |
The password check
A guardrail is a function with @input_guardrail. It returns a GuardrailFunctionOutput; setting tripwire_triggered=True stops the run.
@input_guardrail
async def no_passwords(ctx, agent, user_input) -> GuardrailFunctionOutput:
text = user_input if isinstance(user_input, str) else str(user_input)
tripped = "password" in text.lower() # True blocks the run
return GuardrailFunctionOutput(output_info={"blocked": tripped},
tripwire_triggered=tripped)The guarded desk
The check goes in the desk's input_guardrails list, so it runs before the model on each run.
desk = Agent(
name="Shop desk",
instructions="Help shoppers with their orders.",
tools=[lookup_order],
input_guardrails=[no_passwords],
model=ShopModel(),
)A safe run and a blocked run
A safe message answers as before; a message with a password raises InputGuardrailTripwireTriggered, which you catch.
print("SAFE:", Runner.run_sync(desk, "Where is my order A17?").final_output)
try:
Runner.run_sync(desk, "My password is hunter2, refund me now")
except InputGuardrailTripwireTriggered:
print("BLOCKED: the input guardrail tripped, the model never ran")The desk refusing a password
One safe run and one blocked run, side by side.
from agents import (Agent, Runner, function_tool, set_tracing_disabled,
input_guardrail, GuardrailFunctionOutput, InputGuardrailTripwireTriggered,
RunContextWrapper)
from shop_model import ShopModel
set_tracing_disabled(True)
@function_tool
def lookup_order(order_id: str) -> str:
"Look up an order by its id."
return f"Order {order_id}: shipped on 3 March, arriving 7 March."
@input_guardrail
async def no_passwords(ctx: RunContextWrapper, agent: Agent, user_input) -> GuardrailFunctionOutput:
text = user_input if isinstance(user_input, str) else str(user_input)
tripped = "password" in text.lower()
return GuardrailFunctionOutput(output_info={"blocked": tripped}, tripwire_triggered=tripped)
desk = Agent(
name="Shop desk",
instructions="Help shoppers with their orders.",
tools=[lookup_order],
input_guardrails=[no_passwords],
model=ShopModel(),
)
print("SAFE:", Runner.run_sync(desk, "Where is my order A17?").final_output)
try:
Runner.run_sync(desk, "My password is hunter2, refund me now")
except InputGuardrailTripwireTriggered:
print("BLOCKED: the input guardrail tripped, the model never ran")
What the guardrail did
- The safe message passed the check, so the run reached the model and the tool answered normally.
- The password message tripped the wire, so the SDK raised
InputGuardrailTripwireTriggeredbefore the model saw a single word. - Catching the exception is how your code turns a trip into a polite refusal instead of a crash.
Input guardrail vs output guardrail
| Guardrail | When it runs, and on what |
|---|---|
| Input guardrail | Before the model, on the run's first agent, checking the incoming message |
| Output guardrail | After the model, on the final answer, checking what the agent produced |
When to guard the desk
- Refuse secrets or unsafe requests before the model reads them, so they never enter a prompt or a log.
- Check the final answer with an output guardrail when a reply must never leak a price, a policy, or a rule.
Related
- Previous: A refund specialist by handoff
- Next: The desk that remembers with a session
- Reference: Guardrails
- Change the banned word from password to your own and watch a new message get blocked.
- Remove the
try/exceptand read the traceback the tripwire raises. - Return
tripwire_triggered=Falsealways and confirm the password message now reaches the model.
You understood something today that you didn't yesterday.