-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix: await cancelled websocket task to prevent resource leak #1955
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: await cancelled websocket task to prevent resource leak #1955
Conversation
The close() method now properly awaits the cancelled websocket task to ensure clean resource cleanup. Previously, the task was cancelled but not awaited, which could lead to resource leaks and "Task was destroyed but it is pending!" warnings. Test results: All 23 tests in tests/realtime/test_openai_realtime.py passed
There was a problem hiding this 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 a resource leak in the WebSocket cleanup process by properly awaiting a cancelled task. When closing a realtime WebSocket connection, the previous implementation would cancel the task but not wait for it to complete, potentially leaving resources unreleased.
Key Changes:
- Added proper await handling after cancelling the websocket task
- Included exception handling for
CancelledErrorto prevent propagation
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
@codex review |
|
Codex Review: Didn't find any major issues. Hooray! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Problem: The _cleanup_tasks() method in OpenAISTTTranscriptionSession was only calling task.cancel() on pending tasks (listener, process_events, stream_audio, connection) but not awaiting them. This could lead to: 1. Unhandled task exception warnings 2. Potential resource leaks (websocket connections, file descriptors) 3. Improper cleanup of background tasks Evidence: - Similar to the recently fixed guardrail tasks cleanup (PR openai#1976) - Similar to the fixed websocket task cleanup (PR openai#1955) - asyncio best practices require awaiting cancelled tasks Solution: 1. Made _cleanup_tasks() async 2. Collect all real asyncio.Task objects that need to be awaited 3. Added await asyncio.gather() with return_exceptions=True to properly collect exceptions from cancelled tasks 4. Updated close() method to await _cleanup_tasks() Testing: - All existing voice/STT tests pass (17 passed) - Uses isinstance check to support mock objects in tests - Follows the same pattern as PR openai#1976 and PR openai#1955
Problem: The _cleanup_tasks() method in OpenAISTTTranscriptionSession was only calling task.cancel() on pending tasks (listener, process_events, stream_audio, connection) but not awaiting them. This could lead to: 1. Unhandled task exception warnings 2. Potential resource leaks (websocket connections, file descriptors) 3. Improper cleanup of background tasks Evidence: - Similar to recently fixed guardrail tasks cleanup (PR openai#1976) - Similar to fixed websocket task cleanup (PR openai#1955) - asyncio best practices require awaiting cancelled tasks Solution: 1. Made _cleanup_tasks() async 2. Collect all real asyncio.Task objects that need to be awaited 3. Added await asyncio.gather() with return_exceptions=True to properly collect exceptions from cancelled tasks 4. Updated close() method to await _cleanup_tasks() Testing: - All existing voice/STT tests pass (17 passed) - Uses isinstance check to support mock objects in tests - Follows the same pattern as PR openai#1976 and PR openai#1955
…asks Problem: The _cleanup_tasks() method in VoiceStreamResult was only calling task.cancel() on pending tasks but not awaiting them. Additionally, _check_errors() could raise CancelledError when checking cancelled tasks. This could lead to: 1. Unhandled task exception warnings 2. Potential resource leaks from abandoned tasks 3. CancelledError masking real exceptions Evidence: - Similar to fixed guardrail tasks cleanup (PR openai#1976) - Similar to fixed voice STT cleanup (PR openai#1977) - Similar to fixed websocket cleanup (PR openai#1955) - Bug documented in .claude/bug-analysis/03-resource-leaks.md Solution: 1. Made _cleanup_tasks() async 2. Collect all real asyncio.Task objects that need to be awaited 3. Added await asyncio.gather() with return_exceptions=True to properly collect exceptions from cancelled tasks 4. Updated _check_errors() to skip cancelled tasks using task.cancelled() check to avoid CancelledError when calling task.exception() 5. Updated stream() async generator to await _cleanup_tasks() Testing: - Linting passes - No breaking changes to public API - Follows same pattern as PR openai#1976, openai#1977, openai#1955
Problem: The _cleanup_tasks() method in OpenAISTTTranscriptionSession was only calling task.cancel() on pending tasks (listener, process_events, stream_audio, connection) but not awaiting them. This could lead to: 1. Unhandled task exception warnings 2. Potential resource leaks (websocket connections, file descriptors) 3. Improper cleanup of background tasks Evidence: - Similar to recently fixed guardrail tasks cleanup (PR openai#1976) - Similar to fixed websocket task cleanup (PR openai#1955) - asyncio best practices require awaiting cancelled tasks Solution: 1. Made _cleanup_tasks() async 2. Collect all real asyncio.Task objects that need to be awaited 3. Added await asyncio.gather() with return_exceptions=True to properly collect exceptions from cancelled tasks 4. Updated close() method to await _cleanup_tasks() Testing: - All existing voice/STT tests pass (17 passed) - Uses isinstance check to support mock objects in tests - Follows the same pattern as PR openai#1976 and PR openai#1955
Summary
Fixed a resource leak in the
OpenAIRealtimeWebSocketModel.close()method where the websocket task was cancelled but not awaited, potentially leaving dangling resources.Problem
When
close()was called, the code would:self._websocket_taskNoneThis could lead to:
Solution
Now properly awaits the cancelled task with exception handling:
This ensures the task is fully cleaned up before being discarded.
Testing
tests/realtime/test_openai_realtime.pypassImpact