1.3.3 Error in console mode: ValueError: signal only works in main thread of the main interpreter
Bug Description
Following the quickstart guide https://docs.livekit.io/agents/start/voice-ai/ doesn't launch in console mode. Starting an agent in console mode on windows fails with error:
(.venv) λ python agent.py console
Agents Starting console mode �
14:13:26 DEBUG asyncio Using proactor: IocpProactor
INFO livekit.agents starting worker {"version": "1.3.3", "rtc-version": "1.0.19"}
INFO livekit.agents starting inference executor
Exception in thread Thread-1 (_worker_thread):
Traceback (most recent call last):
File "C:\Users\timothya\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1045, in _bootstrap_inner
self.run()
File "C:\Users\timothya\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 982, in run
self._target(*self._args, **self._kwargs)
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\cli\cli.py", line 1145, in _worker_thread
self._loop.run_until_complete(_async_main())
File "C:\Users\timothya\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 654, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\cli\cli.py", line 1142, in _async_main
await self._server.run(devmode=True, unregistered=True)
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\worker.py", line 627, in run
await self._inference_executor.start()
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\ipc\supervised_proc.py", line 128, in start
await asyncio.shield(self._start())
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\ipc\supervised_proc.py", line 151, in _start
with _mask_ctrl_c():
File "C:\Users\timothya\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 137, in __enter__
return next(self.gen)
^^^^^^^^^^^^^^
File "C:\git\lk-test\.venv\Lib\site-packages\livekit\agents\ipc\supervised_proc.py", line 42, in _mask_ctrl_c
old = signal.signal(signal.SIGINT, signal.SIG_IGN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\timothya\AppData\Local\Programs\Python\Python311\Lib\signal.py", line 58, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: signal only works in main thread of the main interpreter
Expected Behavior
Console mode start successfully.
Reproduction Steps
On windows, python 3.11.9
Manually create .env file with LIVEKIT_API_KEY, LIVEKIT_API_SECRET, and LIVEKIT_URL
Create agent.py with the example code (modifying the load_dotenv line to load_dotenv() since my env file was named .env
set PIPENV_VENV_IN_PROJECT=1
pipenv --python <PATH_TO_3.11.9_EXE>
pipenv install livekit-agents[silero,turn-detector]~=1.2 livekit-plugins-noise-cancellation~=0.2 python-dotenv
pipenv shell
python agent.py download-files
python agent.py console
Fails with the above error
Operating System
Windows 10
Models Used
stt="assemblyai/universal-streaming:en" llm="openai/gpt-4.1-mini" tts="cartesia/sonic-3:9626c31c-bec5-4cca-baa8-f8ba9e84c8bc" vad=silero.VAD.load() turn_detection=MultilingualModel()
Package Versions
livekit-agents==1.3.3
livekit-plugins-noise-cancellation==0.2.5
python-dotenv==1.2.1
Session/Room/Call IDs
No response
Proposed Solution
Additional Context
Screenshots and Recordings
No response
same here
+1
same, it used to work fine for me
v1.3.4 still has this issue
I think we are facing a similar issue when upgrading to 1.3.4, the LIVEKIT_URL env is not being correctly used and instead it defaults to the default value of ws://localhost:7880 and fails to start
same here
i did this patch to make it works on windows, just after the imports of the agent.py add:
if os.name == "nt": from livekit.agents.ipc import supervised_proc
@contextlib.contextmanager
def _safe_mask_ctrl_c():
try:
old = signal.signal(signal.SIGINT, signal.SIG_IGN)
except (ValueError, RuntimeError):
yield
else:
try:
yield
finally:
signal.signal(signal.SIGINT, old)
supervised_proc._mask_ctrl_c = _safe_mask_ctrl_c
this would substitute the function mask_ctrl_c of the supervised_proc with this _safe_mask_ctrl_c defined here , that simply skip the error of signal in case of an error. The instruction signal.signal(signal.SIGINT, signal.SIG_IGN), should prevent the CTRL C in the terminal. Let me know if it works for you too.