-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Description
Last night I upgraded to .17, after upgrade I made some config changes which caused parsing error, I fixed them in few mins and restarted. Today morning I found that all my recording except today are gone, after some troubleshooting I found that deletion happened around same time when error happened. I restarted docker couple of times so dont have logs but after spending some time, I suspect that it happened because in safe mode deletion job ran with default values.
When Frigate starts with an invalid config file, it enters safe mode with cameras: {} and all retention defaults set to days=0. The recording cleanup thread still runs in safe mode and deletes all recordings, causing permanent data loss.
Steps to Reproduce
- Have a working Frigate instance with recordings spanning multiple days (e.g.,
continuous.days=15) - Edit config.yaml and introduce a syntax/validation error (e.g., while replacing hardcoded passwords with environment variables)
- Restart Frigate (
docker restart frigate) - Frigate detects the
ValidationError, prints config errors, and starts in safe mode - Wait for the recording cleanup cycle to run (up to
expire_intervalminutes, default 60) - Fix the config error and restart Frigate
- All recordings are gone
Root Cause
Two issues combine to cause this:
1. Safe mode config has cameras: {} and retention defaults of days=0
In frigate/config/camera/record.py:
class RecordRetainConfig(FrigateBaseModel):
days: float = Field(default=0, ge=0, title="Default retention period.")In frigate/config/config.py line 798, safe mode creates:
safe_config = {"safe_mode": True, "cameras": {}, "mqtt": {"enabled": False}}2. expire_recordings() treats all cameras as "deleted" when cameras: {}
In frigate/record/cleanup.py lines 274-306:
expire_days = max(
self.config.record.continuous.days, self.config.record.motion.days
) # Both are 0 in safe mode → expire_days = 0
expire_before = (
datetime.datetime.now() - datetime.timedelta(days=expire_days)
).timestamp() # expire_before = now
no_camera_recordings = Recordings.select(...).where(
Recordings.camera.not_in(list(self.config.cameras.keys())), # empty list → matches ALL cameras
Recordings.end_time < expire_before, # now → matches all completed recordings
)With cameras: {}, every camera is "not in config" and treated as deleted. With days=0, expire_before = now, so every completed recording is deleted.
3. No safe mode check in app startup
RecordingCleanup starts unconditionally in app.py line 558 — there is no check for safe_mode.
Reproduction Test
I wrote a standalone test that confirms the bug using Frigate's actual models and cleanup query logic:
# With days=0 (safe mode defaults):
# 359 of 360 recordings marked for deletion (99.7%)
# Only the in-progress recording survives
# With days=15 (intended config):
# 0 recordings marked for deletionImpact
A momentary config syntax error (even just a typo while editing) followed by a Frigate restart causes permanent loss of all recorded footage across all cameras. The user has no warning and no recovery path.
Existing Safety Check Gap
sync_recordings() in frigate/record/util.py already has a >50% deletion guard:
if float(len(recordings_to_delete)) / max(1, recordings.count()) > 0.5:
logger.warning("Deleting ...% of recordings DB entries, could be due to configuration error. Aborting...")
return FalseBut the regular retention-based cleanup in expire_recordings() has no such guard.
Suggested Fix
Any combination of:
- Skip recording cleanup in safe mode — if
self.config.safe_modeis True, skipexpire_recordings()entirely - Treat
days=0as "no cleanup" — if bothcontinuous.daysandmotion.daysare 0, log a warning and return early instead of deleting everything - Add a percentage guard to
expire_existing_camera_recordings()similar to whatsync_recordings()already has
Fix #1 is the simplest and most defensive — safe mode should be read-only as much as possible.
Environment
- Frigate version: 0.17
- Trigger: Config edit to replace hardcoded passwords with env variables, syntax error for a few minutes