From f1e2240945ac73976fc1404e180daa6261379c15 Mon Sep 17 00:00:00 2001 From: Rob Arnold <326113+robarnold@users.noreply.github.com> Date: Tue, 12 May 2026 11:34:46 -0700 Subject: [PATCH] Gracefully handle transiently failing exists calls (#23172) I have a very repeatable reproduction of an issue where most of my cameras show a "No frames have been received, check error logs" image in the UI, but restreaming in HomeAssistant is working flawlessly. The only errors in the logs I saw were some like this: `OSError: [Errno 121] Remote I/O error`. Doing a bit more debugging, it looked like Frigate was failing to create the thumbnail directory for a camera because it already existed. This error was a clue as to the class of error. I was surprised to learn that `os.path.exists` [silently suppresses errors from os.stat and returns False](https://github.com/python/cpython/blob/main/Lib/genericpath.py#L22). This makes for a plausible series of events: a transient stat call fails, so Frigate takes the creation path, which gets upset that the directory already exists. I found a few other possible cases to fix but did not make an exhaustive search. It seems that this `exist_ok` flag is used elsewhere within Frigate so I thought it would be a good solution. AI disclosure: I used AI to diagnose my issue and asked it to translate its init-time patches to the container source into this repo. I verified that its patches solved the problem I was facing. Its theory fits the facts - I am using a distributed file system and I saw the error in my logs. I checked the upstream Python code to verify the error suppression behavior, and read the corresponding Frigate code. I did not use AI to author this commit message/PR description; all diction and typos here are my own. --- frigate/app.py | 2 +- frigate/record/maintainer.py | 3 +-- frigate/track/tracked_object.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frigate/app.py b/frigate/app.py index b6a94ed66..8b5766148 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -144,7 +144,7 @@ class FrigateApp: for d in dirs: if not os.path.exists(d) and not os.path.islink(d): logger.info(f"Creating directory: {d}") - os.makedirs(d) + os.makedirs(d, exist_ok=True) else: logger.debug(f"Skipping directory: {d}") diff --git a/frigate/record/maintainer.py b/frigate/record/maintainer.py index 6d25622f4..62d4ad8cb 100644 --- a/frigate/record/maintainer.py +++ b/frigate/record/maintainer.py @@ -610,8 +610,7 @@ class RecordingMaintainer(threading.Thread): camera, ) - if not os.path.exists(directory): - os.makedirs(directory) + os.makedirs(directory, exist_ok=True) # file will be in utc due to start_time being in utc file_name = f"{start_time.strftime('%M.%S.mp4')}" diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index a041e5802..03117df69 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -531,8 +531,7 @@ class TrackedObject: directory = os.path.join(THUMB_DIR, self.camera_config.name) - if not os.path.exists(directory): - os.makedirs(directory) + os.makedirs(directory, exist_ok=True) thumb_bytes = self.get_thumbnail("webp")