What β extend remove()'s not-found no-op treatment to purgeUnreferenced()'s own bucket.delete() call.
Why β a consumer running both the sweep and an independent retention job can race: the other job deletes a blob between the sweep's candidate scan and its delete pass. Today that surfaces as a recurring deleteFailed/error-log entry on every such race β exactly the noise class remove()'s no-op path (same file) was built to prevent. Fails closed (nothing wrongly deleted); noise-only.
Scope β modules/uploads/repositories/uploads.repository.js purgeUnreferenced(): catch the GridFS not-found error on bucket.delete() β debug log + count as already-gone, continue the pass; genuine bucket failures still surface. One unit test: mid-sweep concurrent deletion β no error log, sweep completes, counts accurate.
Surfaced by post-merge review of #4017 (2026-08-05).
What β extend
remove()'s not-found no-op treatment topurgeUnreferenced()'s ownbucket.delete()call.Why β a consumer running both the sweep and an independent retention job can race: the other job deletes a blob between the sweep's candidate scan and its delete pass. Today that surfaces as a recurring
deleteFailed/error-log entry on every such race β exactly the noise classremove()'s no-op path (same file) was built to prevent. Fails closed (nothing wrongly deleted); noise-only.Scope β
modules/uploads/repositories/uploads.repository.jspurgeUnreferenced(): catch the GridFS not-found error onbucket.delete()β debug log + count as already-gone, continue the pass; genuine bucket failures still surface. One unit test: mid-sweep concurrent deletion β no error log, sweep completes, counts accurate.Surfaced by post-merge review of #4017 (2026-08-05).