Skip to content

Recording cleanup deletes all recordings when config fails to parse (safe mode + days=0 default) #22490

@bains-dupinder

Description

@bains-dupinder

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

  1. Have a working Frigate instance with recordings spanning multiple days (e.g., continuous.days=15)
  2. Edit config.yaml and introduce a syntax/validation error (e.g., while replacing hardcoded passwords with environment variables)
  3. Restart Frigate (docker restart frigate)
  4. Frigate detects the ValidationError, prints config errors, and starts in safe mode
  5. Wait for the recording cleanup cycle to run (up to expire_interval minutes, default 60)
  6. Fix the config error and restart Frigate
  7. 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 deletion

Impact

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 False

But the regular retention-based cleanup in expire_recordings() has no such guard.

Suggested Fix

Any combination of:

  1. Skip recording cleanup in safe mode — if self.config.safe_mode is True, skip expire_recordings() entirely
  2. Treat days=0 as "no cleanup" — if both continuous.days and motion.days are 0, log a warning and return early instead of deleting everything
  3. Add a percentage guard to expire_existing_camera_recordings() similar to what sync_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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions