{"record":{"id":"6fa64b907a2bf2d6","repo":"kopia/kopia","slug":"unable-to-delete-blob-v","errorCode":null,"errorMessage":"unable to delete blob %v","messagePattern":"unable to delete blob (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/content/indexblob/index_blob_manager_v0.go","lineNumber":412,"sourceCode":"\t\tBlobIDs:             blobIDs,\n\t\tCleanupScheduleTime: cleanupScheduleTime,\n\t})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"unable to marshal cleanup log bytes\")\n\t}\n\n\tif _, err := m.enc.EncryptAndWriteBlob(ctx, gather.FromSlice(payload), V0CleanupBlobPrefix, \"\"); err != nil {\n\t\treturn errors.Wrap(err, \"unable to cleanup log\")\n\t}\n\n\treturn nil\n}\n\nfunc (m *ManagerV0) deleteBlobsFromStorageAndCache(ctx context.Context, blobIDs []blob.ID) error {\n\tfor _, blobID := range blobIDs {\n\t\tif err := m.st.DeleteBlob(ctx, blobID); err != nil && !errors.Is(err, blob.ErrBlobNotFound) {\n\t\t\tcontentlog.Log2(ctx, m.log, \"delete-blob failed\", blobparam.BlobID(\"blobID\", blobID), logparam.Error(\"err\", err))\n\t\t\treturn errors.Wrapf(err, \"unable to delete blob %v\", blobID)\n\t\t}\n\n\t\tcontentlog.Log1(ctx, m.log, \"delete-blob succeeded\", blobparam.BlobID(\"blobID\", blobID))\n\t}\n\n\treturn nil\n}\n\nfunc (m *ManagerV0) cleanup(ctx context.Context, maxEventualConsistencySettleTime time.Duration) error {\n\tallCleanupBlobs, err := blob.ListAllBlobs(ctx, m.st, V0CleanupBlobPrefix)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"error listing cleanup blobs\")\n\t}\n\n\t// determine latest storage write time of a cleanup blob\n\tvar latestStorageWriteTimestamp time.Time\n\n\tfor _, cb := range allCleanupBlobs {","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/repo/content/indexblob/index_blob_manager_v0.go#L394-L430","documentation":"deleteBlobsFromStorageAndCache failed to delete a specific blob from the underlying blob storage (m.st.DeleteBlob). Errors equal to blob.ErrBlobNotFound are deliberately tolerated (already deleted); any other error is wrapped with the blob ID. This aborts the delete loop, leaving remaining blobs undeleted.","triggerScenarios":"m.st.DeleteBlob(ctx, blobID) returns a non-ErrBlobNotFound error during deleteOldBlobs or cleanup — e.g. storage API failure, permission denial, network error, or a conditional-delete conflict.","commonSituations":"Cloud credentials lacking delete permission (write-only or read-only policy), blob locked by an immutability/retention policy (S3 Object Lock, WORM), transient provider 5xx, or eventual-consistency races where a replica reports the blob in a conflicting state.","solutions":["Read the blob ID from the message and the wrapped provider error for the root cause.","Verify the storage credentials/role permit DeleteBlob operations.","Check for immutability/retention policies (S3 Object Lock, Azure immutability) on the bucket/container.","Retry the operation; missing blobs are already tolerated so a rerun only targets the remaining blobs."],"exampleFix":"// before\nif err := m.st.DeleteBlob(ctx, blobID); err != nil && !errors.Is(err, blob.ErrBlobNotFound) {\n\treturn errors.Wrapf(err, \"unable to delete blob %v\", blobID)\n}\n// after\nif err := m.st.DeleteBlob(ctx, blobID); err != nil && !errors.Is(err, blob.ErrBlobNotFound) {\n\tif retry.IsTransient(err) {\n\t\tcontinue // or back off and retry the whole pass\n\t}\n\treturn errors.Wrapf(err, \"unable to delete blob %v\", blobID)\n}","handlingStrategy":"retry","validationCode":"// Go: verify delete permission before GC passes\nif err := probeBlobDelete(ctx, m.st, testBlobID); err != nil {\n\treturn fmt.Errorf(\"storage delete probe failed: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"unable to delete blob \") { // back off and retry; ErrBlobNotFound tolerated on rerun\n\tbackoff.Retry(func() error { return mgr.Compact(ctx) }, backoff.WithMaxRetries(3, time.Second))\n}","preventionTips":["Grant storage credentials delete permissions, not just read/write","Avoid S3 Object Lock/immutability policies on kopia-managed buckets used for GC","Alert on provider delete-API error rates"],"tags":["blob-storage","delete","permissions"],"backgroundTag":"file-delete-failed","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}