---
type: email
from: Lucas Wang <notifications@github.com>
subject: [openai/openai-agents-python] Fix: Properly await cancelled guardrail

 tasks during cleanup (PR #1976)
received: 2026-03-06 08:13:36
date_header: Wed, 22 Oct 2025 09:37:10 -0700
priority: normal
status: awaiting_approval
uid: 33461
---

## Email Content

**From:** Lucas Wang <notifications@github.com>
**Subject:** [openai/openai-agents-python] Fix: Properly await cancelled guardrail

 tasks during cleanup (PR #1976)
**Date:** Wed, 22 Oct 2025 09:37:10 -0700

### Body (preview)

## Summary



Fixes a resource leak bug where guardrail tasks were cancelled but not properly awaited during cleanup, which could lead to unhandled task exception warnings and memory leaks.



## Problem



The `_cleanup_guardrail_tasks()` method in `RealtimeSession` (`src/agents/realtime/session.py:749`) was only calling `task.cancel()` on pending guardrail tasks but not awaiting them. According to Python&#39;s asyncio best practices, cancelled tasks should be awaited to properly collect exceptions.



## Evidence



1. **Test code shows correct pattern**: `tests/realtime/test_session.py:1199` already demonstrates the proper cleanup:

   ```python

   await asyncio.gather(*session._guardrail_tasks, return_exceptions=True)

   ```



2. **Similar pattern used elsewhere**: The WebSocket task cleanup in `openai_realtime.py:519-523` follows the same pattern of cancel + await.



## Solution



1. Changed `_cleanup_guardrail_tasks()` from sync to async method

2. Added `await asyncio.gather(*self._guardrail_tasks, return_exceptions=True)` after cancelling to properly collect exceptions

3. Updated the caller (`_cleanup()`) to await the cleanup method



## Testing



**New tests** in `tests/realtime/test_guardrail_cleanup.py`:

- ✅ Test 1: Verify cancelled tasks are properly awaited

- ✅ Test 2: Verify exceptions during cleanup are handled  

- ✅ Test 3: Verify multiple concurrent tasks are cleaned up



**Existing tests**: All 8 guardrail tests in `test_session.py` continue to pass.



## Technical Details



**Before:**

```python

def _cleanup_guardrail_tasks(self) -&gt; None:

    for task in self._guardrail_tasks:

        if not task.done():

            task.cancel()

    self._guardrail_tasks.clear()

```



**After:**

```python

async def _cleanup_guardrail_tasks(self) -&gt; None:

    for task in self._guardrail_tasks:

        if not task.done():

            task.cancel()

    

    # Wait for all tasks to complete and collect any exceptions

    if self._g

---

## Suggested Actions
- [ ] Reply
- [ ] Archive
- [ ] Escalate
