overleaf/overleaf · error

Blob ${hash} is not backed up for project ${projectId} - use

Error message

Blob ${hash} is not backed up for project ${projectId} - use --force to delete anyway

What it means

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.

Source

Thrown at services/history-v1/storage/scripts/remove_backup_blobs_from_wrong_path.mjs:180

  if (historyId === projectId) {
    throw new Error(
      `Project ID and history ID are the same for ${projectId} - use --force to delete anyway`
    )
  }

  // TODO: fix assert.postgresId to handle integers better and then stop coercing to string below
  assert.postgresId(
    `${historyId}`,
    `History ID ${historyId} does not appear to be for a postgres project`
  )

  try {
    await verifyBlobs(`${historyId}`, [hash])
  } catch (error) {
    if (args.verbose) {
      console.error(error)
    }
    throw new Error(
      `Blob ${hash} is not backed up for project ${projectId} - use --force to delete anyway`
    )
  }
}

if (!args.commit) {
  console.log('DRY RUN: provide --commit to perform operations')
}

if (args.force) {
  console.log(
    'WARNING: --force is enabled, blobs will be deleted regardless of backup status'
  )
  await setTimeout(5_000)
}

let deleted = 0
let errors = 0

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Run a backup for the project so the blob exists in the backup store, then retry.
  2. Re-run with --force only if you accept permanent loss of that blob's backup.
  3. Check backup bucket configuration (S3/GCS creds, bucket name) used by the backup persistor.
  4. Confirm the hash/historyId pair is correct — the blob may be backed up under a different project.

Example fix

// before
node remove_backup_blobs_from_wrong_path.mjs --project-id X --hash h1
// after
node verify_backup_blob.mjs --historyId H --hashes h1   # confirm backup exists first
node remove_backup_blobs_from_wrong_path.mjs --project-id X --hash h1
Defensive patterns

Strategy: validation

Validate before calling

try {
  await verifyBlobs(historyId, [hash])
  console.log(`${hash} backed up; safe to delete`)
} catch {
  console.error(`${hash} NOT backed up; aborting delete`)
}

Try / catch

try {
  await canDeleteBlob(projectId, hash)
} catch (err) {
  if (err.message.includes('not backed up')) {
    console.warn(`Skipping ${hash}: no backup exists`)
  } else throw err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03). Data as JSON: /api/errors/2765538173408b81. Report an issue: GitHub.