From 4c47c6c85dfa6177d2692763aee982c2877164d0 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:36:21 -0500 Subject: [PATCH] snapshot websocket clients under the manager lock in the output loop The output loop iterated ws4py's live websocket dict without its lock, so a client connecting or disconnecting mid-iteration raised "dictionary changed size during iteration" and killed the output loop. It now copies the clients under manager.lock once per frame and skips terminated ones, matching the jsmpeg and birdseye broadcasters. --- frigate/output/output.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frigate/output/output.py b/frigate/output/output.py index b40a0f4cd0..0872ca7b91 100644 --- a/frigate/output/output.py +++ b/frigate/output/output.py @@ -285,11 +285,16 @@ class OutputProcess(FrigateProcess): ) preview_write_times[camera] = frame_time + manager = websocket_server.manager + with manager.lock: + websockets = list(manager.websockets.values()) + # send camera frame to ffmpeg process if websockets are connected if any( - ws.environ["PATH_INFO"].endswith(camera) + not ws.terminated + and ws.environ["PATH_INFO"].endswith(camera) and ws_has_camera_access(ws, camera, self.config) - for ws in websocket_server.manager + for ws in websockets ): # write to the converter for the camera if clients are listening to the specific camera jsmpeg_cameras[camera].write_frame(frame.tobytes()) @@ -301,9 +306,10 @@ class OutputProcess(FrigateProcess): and ( self.config.birdseye.restream or any( - ws.environ["PATH_INFO"].endswith("birdseye") + not ws.terminated + and ws.environ["PATH_INFO"].endswith("birdseye") and ws_has_camera_access(ws, "birdseye", self.config) - for ws in websocket_server.manager + for ws in websockets ) ) ):