From 4e87fca987b4035d3355a594ef8a25b5994b1a08 Mon Sep 17 00:00:00 2001 From: shivam Date: Thu, 12 Mar 2026 15:49:55 -0700 Subject: [PATCH] fix(proxy): Windows compatibility for Prisma engine watchdog Guard os.waitpid and os.WNOHANG usage with sys.platform check. These APIs are Unix-only; on Windows they cause AttributeError and prevent proxy startup. - _try_waitpid_watch: return False on Windows, fall back to os.kill polling - _reap_all_zombies: return empty set on Windows (no zombies) Add unit tests for Windows path. Made-with: Cursor --- litellm/proxy/utils.py | 9 +++++++++ .../proxy/test_prisma_engine_watchdog.py | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 00ca9043cab..b9a1bfb9062 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -5,6 +5,7 @@ import inspect import json import os import smtplib +import sys import threading import time import traceback @@ -3663,7 +3664,11 @@ class PrismaClient: Returns a set of reaped PIDs. As PID 1 in Docker (or any process that spawns children), we must reap ALL terminated children to prevent zombie accumulation. + + No-op on Windows: os.waitpid and os.WNOHANG are Unix-only. """ + if sys.platform == "win32": + return set() reaped: set = set() while True: try: @@ -3684,7 +3689,11 @@ class PrismaClient: via call_soon_threadsafe. Returns True if the thread was started, False on failure. + On Windows, returns False immediately (os.waitpid/WNOHANG are Unix-only); + caller falls back to os.kill polling. """ + if sys.platform == "win32": + return False try: probe_pid, _ = os.waitpid(pid, os.WNOHANG) except ChildProcessError: diff --git a/tests/litellm/proxy/test_prisma_engine_watchdog.py b/tests/litellm/proxy/test_prisma_engine_watchdog.py index fb5ace05967..f4032c78031 100644 --- a/tests/litellm/proxy/test_prisma_engine_watchdog.py +++ b/tests/litellm/proxy/test_prisma_engine_watchdog.py @@ -342,10 +342,25 @@ async def test_stop_watchdog_task_also_stops_engine_watcher( # --------------------------------------------------------------------------- -# waitpid thread (cross-platform) +# waitpid thread (Unix only; Windows falls back to os.kill polling) # --------------------------------------------------------------------------- +def test_try_waitpid_watch_returns_false_on_windows(engine_client): + """_try_waitpid_watch returns False on Windows (os.waitpid/WNOHANG unavailable).""" + with patch("sys.platform", "win32"): + result = engine_client._try_waitpid_watch(1234) + assert result is False + assert engine_client._engine_wait_thread is None + + +def test_reap_all_zombies_returns_empty_on_windows(engine_client): + """_reap_all_zombies returns empty set on Windows (waitpid unavailable).""" + with patch("sys.platform", "win32"): + reaped = PrismaClient._reap_all_zombies() + assert reaped == set() + + def test_try_waitpid_watch_returns_false_when_not_child(engine_client): """_try_waitpid_watch returns False when PID is not our child process.""" engine_client._engine_pid = 9999