Skip to content

fix: re-raise output guardrail exceptions in streaming path#2752

Closed
gn00295120 wants to merge 1 commit intoopenai:mainfrom
gn00295120:fix/streaming-output-guardrail-bypass
Closed

fix: re-raise output guardrail exceptions in streaming path#2752
gn00295120 wants to merge 1 commit intoopenai:mainfrom
gn00295120:fix/streaming-output-guardrail-bypass

Conversation

@gn00295120
Copy link
Copy Markdown
Contributor

Summary

Two critical bug fixes:

1. Streaming Output Guardrail Bypass (HIGH severity)

File: src/agents/run.py

Before: In the streaming path (AgentRunner._run_streamed_impl), a bare except Exception clause caught and silently discarded OutputGuardrailTripwireTriggered, allowing the run to continue with final_output set despite the guardrail having tripped:

try:
    output_guardrail_results = await streamed_result._output_guardrails_task
except Exception:
    # Exceptions will be checked in the stream_events loop
    output_guardrail_results = []

After: OutputGuardrailTripwireTriggered is re-raised immediately, stopping the run loop. Only truly unexpected errors are caught and logged:

try:
    output_guardrail_results = await streamed_result._output_guardrails_task
except OutputGuardrailTripwireTriggered:
    raise
except Exception:
    logger.error("Unexpected error in output guardrails", exc_info=True)
    output_guardrail_results = []

2. SQLiteSession Race Condition

File: src/agents/memory/sqlite_session.py

Before: File-based SQLite sessions created a new threading.Lock() on every method call, providing zero mutual exclusion:

with self._lock if self._is_memory_db else threading.Lock():

After: A shared self._file_lock is created once in __init__ and reused across all 4 call sites:

with self._lock if self._is_memory_db else self._file_lock:

Test plan

  • New test tests/test_streaming_output_guardrail_fix.py with 3 cases:
    • Streaming: agent-level output guardrail trip raises OutputGuardrailTripwireTriggered
    • Streaming: run-config-level output guardrail trip raises OutputGuardrailTripwireTriggered
    • Streaming: non-tripping guardrail completes normally (no false positive)
  • All 83 existing tests in test_agent_runner_streamed, test_agent_runner, test_guardrails, and test_session pass

Copilot AI review requested due to automatic review settings March 22, 2026 07:17
@github-actions github-actions Bot added bug Something isn't working feature:sessions labels Mar 22, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes error-handling and concurrency issues in the Agents SDK, primarily ensuring streaming runs correctly surface output-guardrail tripwires and improving session/tool configuration robustness.

Changes:

  • Re-raise OutputGuardrailTripwireTriggered in the streaming final-output path instead of swallowing it, and log other unexpected guardrail failures.
  • Fix ineffective locking in file-based SQLiteSession operations by reusing a shared lock instead of allocating a new lock per call.
  • Add Agent.tools element validation (and new tests) to fail fast on invalid tool entries.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/agents/run.py Adjusts streaming final-output guardrail exception handling to re-raise tripwire exceptions and log unexpected errors.
src/agents/memory/sqlite_session.py Introduces a persistent file-lock to provide real mutual exclusion for file-based SQLite sessions.
src/agents/agent.py Adds per-element validation for Agent.tools and raises UserError with guidance on invalid entries.
tests/test_streaming_output_guardrail_fix.py Adds regression tests ensuring streaming output guardrail tripwires propagate and non-tripping guardrails don’t false-positive.
tests/test_agent_config.py Adds tests to ensure invalid tool-list contents fail during Agent initialization (Issue #1443).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/agents/agent.py Outdated
Comment thread src/agents/agent.py Outdated
Comment thread src/agents/run.py Outdated
- Fix OutputGuardrailTripwireTriggered silently swallowed by bare
  except Exception in run_loop.py streaming path
- Re-raise asyncio.CancelledError for Python 3.9/3.10 compatibility
- Fix SQLiteSession race condition: replace per-call threading.Lock()
  with shared self._file_lock for file-based databases
@gn00295120 gn00295120 force-pushed the fix/streaming-output-guardrail-bypass branch from 63d7513 to 8650178 Compare March 22, 2026 07:53
@seratch seratch marked this pull request as draft March 23, 2026 03:48
@seratch
Copy link
Copy Markdown
Member

seratch commented Mar 23, 2026

Thanks for pointing this issue out, and this should be fixed. Regarding the solution, I think #2758 is more robust, so let me go with the PR. Thanks again for reporting this issue.

@seratch seratch closed this Mar 23, 2026
@gn00295120
Copy link
Copy Markdown
Contributor Author

Thanks for pointing this issue out, and this should be fixed. Regarding the solution, I think #2758 is more robust, so let me go with the PR. Thanks again for reporting this issue.

You're welcome. Long time no see👍

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

Labels

bug Something isn't working feature:sessions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants