Skip to content

Commit 57b0fd1

Browse files
vdusekclaude
andcommitted
fix: handle TimeoutError in Actor __aexit__ to prevent resource leaks
When Actor cleanup exceeded `_cleanup_timeout`, the unhandled `TimeoutError` from `asyncio.wait_for` left the Actor in a broken state: `_is_initialized` was never reset, event/charging managers were left partially shut down, and `sys.exit()` was never called. Now the timeout is caught and critical resource cleanup (event manager, charging manager) is attempted even after timeout. `_is_initialized` is reset in a `finally` block to guarantee consistent state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 56aa42e commit 57b0fd1

File tree

1 file changed

+11
-2
lines changed

1 file changed

+11
-2
lines changed

src/apify/_actor.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,17 @@ async def finalize() -> None:
252252
# Persist Actor state
253253
await self._save_actor_state()
254254

255-
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
256-
self._is_initialized = False
255+
try:
256+
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
257+
except TimeoutError:
258+
self.log.warning('Actor cleanup timed out, forcing shutdown of event manager and charging manager')
259+
# Ensure critical resources are cleaned up even after timeout
260+
with suppress(Exception):
261+
await self.event_manager.__aexit__(None, None, None)
262+
with suppress(Exception):
263+
await self._charging_manager_implementation.__aexit__(None, None, None)
264+
finally:
265+
self._is_initialized = False
257266

258267
if self._exit_process:
259268
sys.exit(self.exit_code)

0 commit comments

Comments
 (0)