{"record":{"id":"cae88c2576343d13","repo":"kopia/kopia","slug":"unable-to-extend-retention-period","errorCode":null,"errorMessage":"unable to extend retention period","messagePattern":"unable to extend retention period","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/blob/azure/azure_storage.go","lineNumber":187,"sourceCode":"\t\treturn az.retryDeleteBlob(ctx, b)\n\t}\n\n\treturn err\n}\n\n// ExtendBlobRetention extends a blob retention period.\nfunc (az *azStorage) ExtendBlobRetention(ctx context.Context, b blob.ID, opts blob.ExtendOptions) error {\n\tretainUntilDate := clock.Now().Add(opts.RetentionPeriod).UTC()\n\tmode := azblobblob.ImmutabilityPolicySetting(blob.Locked) // overwrite the S3 values\n\n\t_, err := az.service.ServiceClient().\n\t\tNewContainerClient(az.Container).\n\t\tNewBlobClient(az.getObjectNameString(b)).\n\t\tSetImmutabilityPolicy(ctx, retainUntilDate, &azblobblob.SetImmutabilityPolicyOptions{\n\t\t\tMode: &mode,\n\t\t})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"unable to extend retention period\")\n\t}\n\n\treturn nil\n}\n\nfunc (az *azStorage) getObjectNameString(b blob.ID) string {\n\treturn az.Prefix + string(b)\n}\n\n// ListBlobs list azure blobs with given prefix.\nfunc (az *azStorage) ListBlobs(ctx context.Context, prefix blob.ID, callback func(blob.Metadata) error) error {\n\tprefixStr := az.getObjectNameString(prefix)\n\n\tpager := az.service.NewListBlobsFlatPager(az.container, &azblob.ListBlobsFlatOptions{\n\t\tPrefix: &prefixStr,\n\t\tInclude: azblob.ListBlobsInclude{\n\t\t\tMetadata: true,\n\t\t},","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/repo/blob/azure/azure_storage.go#L169-L205","documentation":"Kopia wraps the Azure Blob SetImmutabilityPolicy call when extending a blob's time-based retention (WORM immutability) so an existing retention window can be lengthened. The underlying Azure SDK rejected or failed the SetImmutabilityPolicy request, so the blob's retention period was not extended. This is a hard failure because shortening or missing immutability windows can break compliance guarantees.","triggerScenarios":"Calling ExtendBlobRetention on a blob that has no immutability policy yet, on a blob locked (Mounted) in immutability mode where policy changes are restricted, with a retainUntilDate earlier than or equal to the current policy date, or when the request fails due to auth/permissions/network issues.","commonSituations":"Storage account without version-level immutability support enabled; attempting to extend retention on an already-locked policy with conflicting settings; RBAC role lacking the immutability policy write permission; transient Azure API errors during retention sweeps.","solutions":["Verify the storage account has version-level immutability enabled and the blob already has an immutability policy","Ensure the new retainUntilDate is strictly later than the current policy's expiry","Check the credentials/RBAC role can write immutability policies (Storage Blob Data Owner)","If the policy is locked, only extensions are allowed — confirm Mode is set appropriately and dates comply","Retry on transient network/5xx errors"],"exampleFix":"// before\nerr := az.extendImmutability(ctx, b, newDate) // fails if newDate <= current expiry\n// after\nif !newDate.After(currentPolicy.ExpiresOn) {\n    return errors.Errorf(\"new retain-until %v must be after current expiry %v\", newDate, currentPolicy.ExpiresOn)\n}\nerr := az.extendImmutability(ctx, b, newDate)","handlingStrategy":"try-catch","validationCode":"// verify blob has an immutability policy and new date is later\nprop, _ := blobClient.GetProperties(ctx, nil)\nif prop.ImmutabilityPolicyExpiresOn == nil {\n    return fmt.Errorf(\"blob %s has no immutability policy to extend\", blobName)\n}\nif !newRetainUntil.After(*prop.ImmutabilityPolicyExpiresOn) {\n    return fmt.Errorf(\"new retain-until %v must be after current %v\", newRetainUntil, *prop.ImmutabilityPolicyExpiresOn)\n}","typeGuard":null,"tryCatchPattern":"err := az.ExtendBlobRetention(ctx, blobID, retainUntil)\nvar azErr azcore.ResponseError\nif errors.As(err, &azErr) {\n    switch azErr.StatusCode {\n    case 409: // conflict: locked policy or non-increasing date\n        // inspect azErr.ErrorCode and adjust retention date\n    case 403: // permission denied on immutability policy write\n    }\n}\nreturn err","preventionTips":["Enable version-level immutability on the storage account before using retention features","Always extend retention with dates strictly later than the current expiry","Grant Storage Blob Data Owner for immutability policy operations","Log the wrapped Azure ResponseError code for diagnosis"],"tags":["azure","blob-storage","immutability","retention-policy"],"backgroundTag":"http-error-response","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"}