Skip to content

Conversation

@pxkundu
Copy link

@pxkundu pxkundu commented Oct 17, 2025

Summary

Fixes #889 - Input guardrails were running in parallel with agent execution, allowing tools to execute even when guardrails should block them. This caused unnecessary token consumption and potential security issues.

Changes

Non-streaming path (Runner.run)

  • Modified to run input guardrails sequentially before _run_single_turn
  • Guardrails now fully complete and check for tripwires before the agent starts

Streaming path (Runner.run_streamed)

  • Changed _run_input_guardrails_with_queue from background task to awaited call
  • Added immediate exception raising when tripwire is triggered in _run_input_guardrails_with_queue
  • Ensures guardrails complete before streaming starts

Impact

Before this fix:

  • Input guardrails ran in parallel with agent execution (using asyncio.gather)
  • Tools could execute even when guardrails triggered
  • LLM calls were made despite guardrail blocking the input
  • Tokens were consumed unnecessarily

After this fix:

  • Input guardrails run and complete first
  • If a guardrail triggers, agent execution never starts
  • No LLM calls or tool executions when input is blocked
  • Proper security boundary enforcement

Testing

  • ✅ All existing agent runner tests pass (50/50)
  • ✅ All guardrail tests pass (8/8)
  • ✅ Maintains backward compatibility for passing guardrails
  • ✅ Properly blocks execution when guardrails trigger in both streaming and non-streaming modes

Reviewers

This addresses the concern raised by multiple users in #889 where tools were executing despite guardrail tripwires being triggered. The fix ensures guardrails work as documented: blocking agent execution before any resources are consumed.

This fixes issue openai#889 where input guardrails were running in parallel
with agent execution, allowing tools to run even when guardrails should
block them.

Changes:
- Modified non-streaming path to run guardrails sequentially before
  _run_single_turn
- Modified streaming path to await guardrails completion before
  starting the streamed agent turn
- Added immediate exception raising in _run_input_guardrails_with_queue
  when tripwire is triggered

This ensures that when a guardrail triggers, no LLM calls or tool
executions happen, preventing unnecessary token consumption and
potential security issues.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR.

Comment on lines 1000 to 1011
if current_turn == 1:
# Run the input guardrails in the background and put the results on the queue
streamed_result._input_guardrails_task = asyncio.create_task(
cls._run_input_guardrails_with_queue(
starting_agent,
starting_agent.input_guardrails + (run_config.input_guardrails or []),
ItemHelpers.input_to_new_input_list(prepared_input),
context_wrapper,
streamed_result,
current_span,
)
# Run input guardrails first, before starting the streamed agent turn.
# This prevents tools from executing when guardrails should block the input.
# We await this to ensure guardrails complete before the agent starts.
await cls._run_input_guardrails_with_queue(
starting_agent,
starting_agent.input_guardrails + (run_config.input_guardrails or []),
ItemHelpers.input_to_new_input_list(prepared_input),
context_wrapper,
streamed_result,
current_span,
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guardrail tripwire causes stream_events hang

Awaiting _run_input_guardrails_with_queue means a tripwire now raises before anything is queued on the streaming event queue. If a caller starts iterating RunResultStreaming.stream_events() while guardrails are still running, the iterator runs _check_errors() once (no error yet) and then awaits self._event_queue.get(). When the guardrail subsequently raises, _start_streaming exits without ever enqueuing a QueueCompleteSentinel or any event, so the consumer remains blocked forever and never receives the InputGuardrailTripwireTriggered exception even though _run_impl_task has failed. Consider notifying the event queue or setting is_complete before raising so that the stream can terminate instead of deadlocking.

Useful? React with 👍 / 👎.

@seratch
Copy link
Member

seratch commented Oct 17, 2025

Thanks for sending this PR. However, getting rid of parallel tool execution by removing asyncio.gather() is not the behavioral change we want to have. We had a few attempts by introducing an option to customize the behavior, but we haven't managed to have a viable solution yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FileSearchTool runs despite InputGuardrailTripwireTriggered

2 participants