From 50dedf350d94bc4393e19c42f1be8a67c0d99cdd Mon Sep 17 00:00:00 2001 From: Om Chauhan Date: Mon, 2 Feb 2026 08:38:54 +0530 Subject: [PATCH] fix: ensure function call timeout task is always cancelled --- src/pipecat/services/llm_service.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pipecat/services/llm_service.py b/src/pipecat/services/llm_service.py index 08da591c5..e354543e4 100644 --- a/src/pipecat/services/llm_service.py +++ b/src/pipecat/services/llm_service.py @@ -681,12 +681,11 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): timeout_task = self.create_task(timeout_handler()) - # Yield to the event loop so the timeout task coroutine gets entered - # before it could be cancelled. Without this, cancelling the task before - # it starts would leave the coroutine in a "never awaited" state. - await asyncio.sleep(0) - try: + # Yield to the event loop so the timeout task coroutine gets entered + # before it could be cancelled. Without this, cancelling the task before + # it starts would leave the coroutine in a "never awaited" state. + await asyncio.sleep(0) if isinstance(item.handler, DirectFunctionWrapper): # Handler is a DirectFunctionWrapper await item.handler.invoke( @@ -722,12 +721,12 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): ) await item.handler(params) except Exception as e: - # Cancel timeout task if it exists - if timeout_task and not timeout_task.done(): - await self.cancel_task(timeout_task) error_message = f"Error executing function call [{runner_item.function_name}]: {e}" logger.error(f"{self} {error_message}") await self.push_error(error_msg=error_message, exception=e, fatal=False) + finally: + if timeout_task and not timeout_task.done(): + await self.cancel_task(timeout_task) async def _cancel_function_call(self, function_name: Optional[str]): cancelled_tasks = set()