{"record":{"id":"2765538173408b81","repo":"overleaf/overleaf","slug":"blob-hash-is-not-backed-up-for-project-projec","errorCode":null,"errorMessage":"Blob ${hash} is not backed up for project ${projectId} - use --force to delete anyway","messagePattern":"Blob (.+?) is not backed up for project (.+?) - use --force to delete anyway","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/history-v1/storage/scripts/remove_backup_blobs_from_wrong_path.mjs","lineNumber":180,"sourceCode":"  if (historyId === projectId) {\n    throw new Error(\n      `Project ID and history ID are the same for ${projectId} - use --force to delete anyway`\n    )\n  }\n\n  // TODO: fix assert.postgresId to handle integers better and then stop coercing to string below\n  assert.postgresId(\n    `${historyId}`,\n    `History ID ${historyId} does not appear to be for a postgres project`\n  )\n\n  try {\n    await verifyBlobs(`${historyId}`, [hash])\n  } catch (error) {\n    if (args.verbose) {\n      console.error(error)\n    }\n    throw new Error(\n      `Blob ${hash} is not backed up for project ${projectId} - use --force to delete anyway`\n    )\n  }\n}\n\nif (!args.commit) {\n  console.log('DRY RUN: provide --commit to perform operations')\n}\n\nif (args.force) {\n  console.log(\n    'WARNING: --force is enabled, blobs will be deleted regardless of backup status'\n  )\n  await setTimeout(5_000)\n}\n\nlet deleted = 0\nlet errors = 0","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/history-v1/storage/scripts/remove_backup_blobs_from_wrong_path.mjs#L162-L198","documentation":"Before deleting a misplaced backup blob, canDeleteBlob calls verifyBlobs to confirm the blob is properly backed up at the correct location. If verification throws (the blob is NOT backed up), the script raises this error so the deletion is aborted — deleting would leave the blob with no backup at all. --force overrides this check.","triggerScenarios":"verifyBlobs(historyId, [hash]) throws because the blob hash is absent from the backup bucket for the given history id — e.g. backup never ran, backup bucket misconfigured, or hash belongs to a different project.","commonSituations":"Partial backup coverage (backup job interrupted); backup bucket name/region mismatch in env; running cleanup before the initial backup completed; hashes collected from the wrong history.","solutions":["Run a backup for the project so the blob exists in the backup store, then retry.","Re-run with --force only if you accept permanent loss of that blob's backup.","Check backup bucket configuration (S3/GCS creds, bucket name) used by the backup persistor.","Confirm the hash/historyId pair is correct — the blob may be backed up under a different project."],"exampleFix":"// before\nnode remove_backup_blobs_from_wrong_path.mjs --project-id X --hash h1\n// after\nnode verify_backup_blob.mjs --historyId H --hashes h1   # confirm backup exists first\nnode remove_backup_blobs_from_wrong_path.mjs --project-id X --hash h1","handlingStrategy":"validation","validationCode":"try {\n  await verifyBlobs(historyId, [hash])\n  console.log(`${hash} backed up; safe to delete`)\n} catch {\n  console.error(`${hash} NOT backed up; aborting delete`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await canDeleteBlob(projectId, hash)\n} catch (err) {\n  if (err.message.includes('not backed up')) {\n    console.warn(`Skipping ${hash}: no backup exists`)\n  } else throw err\n}","preventionTips":["Ensure backup jobs complete before running cleanup scripts.","Run verify_backup_blob.mjs on each hash before deleting.","Check backup bucket env config for the persistor.","Never combine --force with unverified hashes."],"tags":["backup","verification","blob-not-found","maintenance-script"],"backgroundTag":"blob-not-backed-up","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}