-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
pipecat version
v0.0.100 (Unreleased)
Python version
3.12
Operating System
Linux (Docker/Kubernetes on AKS)
Issue description
When using GeminiLiveLLMService (or GeminiLiveVertexLLMService) with a registered function that ends the pipeline (like end_call), a RuntimeWarning is logged because the timeout handler coroutine is never awaited.
Analysis:
The issue appears to be a race condition in LLMService._run_function_call(). When the function call (e.g., end_call) triggers pipeline shutdown via EndTaskFrame/EndFrame, the timeout handler coroutine created for that function call is orphaned before it can be properly cancelled or awaited.
Potential Fix:
The timeout handler task should be properly cancelled in a finally block or when the function call completes, ensuring it doesn't get garbage collected with an unawaited coroutine.
Workaround:
The warning is cosmetic and doesn't affect functionality. Calls complete successfully.
Potential Code Fix:
File: pipecat/services/llm_service.py
Method: _run_function_call() (lines 577-672)
The Bug (Lines 604-612, 669-672)
# Line 612: timeout_task is created
timeout_task = self.create_task(timeout_handler())
# ... handler is called ...
except Exception as e:
# Line 669-672: No finally block!
error_message = f"Error executing function call [...]"
logger.error(...)
await self.push_error(...)
# Function ends WITHOUT cancelling timeout_task if:
# 1. Handler doesn't call result_callback (rare edge case)
# 2. Exception is raised (timeout_task leaks)
# 3. Handler triggers pipeline end (end_call case)The Fix
Add a finally block to ensure timeout_task is always cancelled:
# After line 668, add:
except Exception as e:
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:
# Ensure timeout task is cancelled to prevent "coroutine never awaited" warning
if timeout_task and not timeout_task.done():
await self.cancel_task(timeout_task)Why This Fixes It
When end_call triggers EndTaskFrame/EndFrame:
- The handler completes (or the pipeline starts shutting down)
- The
_run_function_callmethod exits - Without the fix:
timeout_taskis orphaned → RuntimeWarning - With the fix:
finallyblock cancelstimeout_taskcleanly
Reproduction steps
- Register an
end_callfunction that pushesEndTaskFrameandEndFrameto gracefully end the call - During a conversation, trigger the function call (e.g., user says goodbye, LLM invokes
end_call) - Observe the warning in logs
Expected behavior
No warnings - the timeout handler should be properly cancelled or awaited when the pipeline ends.
Actual behavior
/usr/local/lib/python3.12/asyncio/events.py:88: RuntimeWarning: coroutine 'LLMService._run_function_call..timeout_handler' was never awaited
self._context.run(self._callback, *self._args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Logs
2026-01-29 11:17:05.015 | DEBUG | pipecat.observers.loggers.llm_log_observer:on_push_frame:68 - 🧠 GeminiLiveVertexLLMService#5 → LLM GENERATING: ' Bye!' at 114.21s
/usr/local/lib/python3.12/asyncio/events.py:88: RuntimeWarning: coroutine 'LLMService._run_function_call.<locals>.timeout_handler' was never awaited
self._context.run(self._callback, *self._args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2026-01-29 11:17:05.170 | DEBUG | pipecat.services.llm_service:_run_function_call:585 - GeminiLiveVertexLLMService#5 Calling function [end_call:...] with arguments {'reason': 'conversation concluded.'}
2026-01-29 11:17:05.170 | INFO | bots.base_bot:end_call:313 - end_call triggered for Mohammad Hussam (08767872521). Reason: conversation concluded.