{"record":{"id":"a8321892dae3c331","repo":"coollabsio/coolify","slug":"the-s3-storage-used-by-an-existing-backup-is-unava","errorCode":null,"errorMessage":"The S3 storage used by an existing backup is unavailable.","messagePattern":"The S3 storage used by an existing backup is unavailable\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"app/Actions/Shared/DeleteScheduledVolumeBackup.php","lineNumber":57,"sourceCode":"            if ($localFilenames !== []) {\n                $server ??= $backup->server();\n                if (! $server) {\n                    throw new \\RuntimeException('The server is unavailable, so local backup archives cannot be deleted.');\n                }\n\n                deleteBackupsLocally($localFilenames, $server, throwError: true);\n            }\n\n            $s3Executions = $backup->executions()\n                ->with('s3')\n                ->where('s3_uploaded', true)\n                ->where('s3_storage_deleted', false)\n                ->get();\n\n            foreach ($s3Executions->groupBy('s3_storage_id') as $executions) {\n                $s3 = $executions->first()->s3;\n                if (! $s3) {\n                    throw new \\RuntimeException('The S3 storage used by an existing backup is unavailable.');\n                }\n\n                $filenames = $executions->pluck('filename')->filter()->all();\n                if ($filenames !== []) {\n                    deleteBackupsS3($filenames, $s3);\n                }\n            }\n\n            $backup->delete();\n        } finally {\n            $lock->release();\n        }\n    }\n}\n","sourceCodeStart":39,"sourceCodeEnd":72,"githubUrl":"https://github.com/coollabsio/coolify/blob/70b9acc42467278373e00de77abb40684e25b395/app/Actions/Shared/DeleteScheduledVolumeBackup.php#L39-L72","documentation":"Thrown while deleting a volume backup schedule: some executions have s3_uploaded=true and s3_storage_deleted=false, but executions->first()->s3 is null — the S3 storage configuration (S3Storage model) they reference was deleted from Coolify. The action aborts so remote objects in the bucket are not left untracked.","triggerScenarios":"Deleting an S3 storage entry in Settings -> S3 Storage before deleting the backup schedules/executions that uploaded to it; groupBy('s3_storage_id') finds executions whose s3 relation no longer exists.","commonSituations":"Rotating S3 providers: user removes the old storage config assuming backups are independent; cleanup scripts delete storages but not dependent schedules; team membership changes hide the storage while executions still point at its id.","solutions":["Recreate an S3 storage entry with the same credentials/bucket so the action can delete the remote objects, then retry the schedule deletion.","If the bucket is being abandoned, empty the relevant prefix manually in your provider console, then set s3_storage_deleted=true on those executions (or remove the execution rows) and delete the schedule.","Going forward, delete backup schedules that target an S3 storage before deleting the storage itself."],"exampleFix":"// before: deleting the schedule throws because the S3 storage is gone\nDeleteScheduledVolumeBackup::run($backup);\n\n// after: explicitly acknowledge the orphaned remote objects first\nforeach ($backup->executions()->where('s3_uploaded', true)->where('s3_storage_deleted', false)->get() as $execution) {\n    if (is_null($execution->s3)) {\n        // objects already removed by hand (or bucket decommissioned)\n        $execution->update(['s3_storage_deleted' => true]);\n    }\n}\nDeleteScheduledVolumeBackup::run($backup);","handlingStrategy":"validation","validationCode":"// Fail fast if any S3-uploaded execution lost its storage config\n$orphanedS3 = $backup->executions()\n    ->with('s3')\n    ->where('s3_uploaded', true)\n    ->where('s3_storage_deleted', false)\n    ->get()\n    ->filter(fn ($e) => is_null($e->s3))\n    ->isNotEmpty();\n\nif ($orphanedS3) {\n    // recreate the S3 storage or acknowledge manual bucket cleanup first\n}","typeGuard":"function s3ExecutionsResolvable(\\App\\Models\\ScheduledVolumeBackup $backup): bool\n{\n    return $backup->executions()\n        ->with('s3')\n        ->where('s3_uploaded', true)\n        ->where('s3_storage_deleted', false)\n        ->get()\n        ->every(fn ($e) => ! is_null($e->s3));\n}","tryCatchPattern":"try {\n    DeleteScheduledVolumeBackup::run($backup);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'S3 storage')) {\n        report('Schedule '.$backup->id.' references a deleted S3 storage; recreate it or clean the bucket manually.');\n        return;\n    }\n    throw $e;\n}","preventionTips":["Delete backup schedules that upload to an S3 storage before deleting the storage entry itself.","Keep a 'decommission' runbook for S3 storages: empty the prefix, then remove configs and schedules.","Periodically audit for orphaned references: executions where s3_storage_id no longer resolves."],"tags":["backup","volume-backup","s3","orphaned-record"],"backgroundTag":"orphaned-relation","analyzedSha":"70b9acc42467278373e00de77abb40684e25b395","analyzedAt":"2026-08-17T01:41:01.313Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}