kopia/kopia · error

error deleting

Error message

error deleting %v

What it means

Wraps a failure from syncDeleteBlob for a specific BLOB during the delete phase of 'repository sync to --delete', annotating it with the BLOB ID. It means the destination storage refused/failure on DeleteBlob for a reason other than the BLOB already being gone (ErrBlobNotFound is tolerated and returns nil).

Solutions

  1. Inspect the wrapped inner error in debug logs to identify the exact storage failure
  2. Grant delete permissions on the destination (e.g. s3:DeleteObject) or use credentials that have them
  3. Check for object-lock/retention/WORM policies on the destination bucket and remove/relax them if sync --delete is intended
  4. Re-run the sync once the storage issue is resolved

Example fix

// before: IAM policy without delete
{"Effect":"Allow","Action":["s3:ListBucket","s3:GetObject","s3:PutObject"],...}
// after
{"Effect":"Allow","Action":["s3:ListBucket","s3:GetObject","s3:PutObject","s3:DeleteObject"],...}
Defensive patterns

Strategy: validation

Validate before calling

// ensure delete permission exists before running sync --delete
out, err := testDeleteAccess(dstStorage, probeBlobID)
if err != nil || !out { return errors.New("destination lacks delete permission; --delete will fail") }

Try / catch

if err := runSyncDelete(ctx); err != nil {
    if strings.Contains(err.Error(), "error deleting ") {
        log.Printf("delete failed: %v — check delete permissions and retention policies", err)
    }
    return err
}

Prevention

When it happens

Trigger: Running 'kopia repository sync to ... --delete' where dst.DeleteBlob(ctx, m.BlobID) returns a non-ErrBlobNotFound error: permissions lacking delete rights, storage returning access-denied, network failure, or object-lock/retention policies blocking deletion.

Common situations: S3 bucket with Object Lock or versioning-based retention preventing deletes; IAM policy with Put but not DeleteObject; read-only credentials on destination; transient cloud errors.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/45652769515c7c0c. Report an issue: GitHub.

Appendix: source

Thrown at cli/command_repository_sync.go:274

				progressMutex.Lock()

				if est, ok := tt.Estimate(float64(bytesCopied), float64(totalBytes)); ok {
					eta = fmt.Sprintf("%v (%v)", est.Remaining, formatTimestamp(est.EstimatedEndTime))
					speed = units.BytesPerSecondsString(est.SpeedPerSecond)
				}

				c.outputSyncProgress(
					fmt.Sprintf("  Copied %v blobs (%v), Speed: %v, ETA: %v",
						numBlobs, units.BytesString(bytesCopied), speed, eta))

				progressMutex.Unlock()
			}

			for m := range deleteCh {
				log(ctx).Debugf("[%v] Deleting %v (%v bytes)...\n", workerID, m.BlobID, m.Length)

				if err := syncDeleteBlob(ctx, m, dst); err != nil {
					return errors.Wrapf(err, "error deleting %v", m.BlobID)
				}
			}

			return nil
		})
	}

	if err := eg.Wait(); err != nil {
		return errors.Wrap(err, "error copying blobs")
	}

	return nil
}

func sliceToChannel(ctx context.Context, md []blob.Metadata) chan blob.Metadata {
	ch := make(chan blob.Metadata)

	go func() {

View on GitHub (pinned to 82495e54b5)