kopia/kopia · warning · ErrUnsupportedObjectLock

object locking unsupported

Error message

object locking unsupported

What it means

ErrUnsupportedObjectLock is a sentinel error returned by blob storage providers that do not implement Object Lock functionality. ExtendBlobRetention in DefaultProviderImplementation always returns it, signaling that the backing storage cannot extend blob retention.

Solutions

  1. Do not call ExtendBlobRetention unless the storage provider supports Object Lock; check provider capabilities first
  2. Use an S3-backed provider with Object Lock enabled on the bucket
  3. Handle the error gracefully and treat retention extension as a no-op for this provider

Example fix

// before
err := storage.ExtendBlobRetention(ctx, blobID, opts)
// after
if extender, ok := storage.(interface{ ExtendBlobRetention(context.Context, blob.ID, blob.ExtendOptions) error }); ok && !isDefaultProvider(storage) {
    err := extender.ExtendBlobRetention(ctx, blobID, opts)
} else {
    log.Printf("retention extension unsupported for this storage")
}
Defensive patterns

Strategy: fallback

Validate before calling

_, ok := storage.(interface{ ExtendBlobRetention(context.Context, blob.ID, blob.ExtendOptions) error })
if !ok { /* provider does not support retention extension */ }

Type guard

func supportsRetentionExt(s blob.Storage) bool {
    _, ok := s.(interface{ ExtendBlobRetention(context.Context, blob.ID, blob.ExtendOptions) error })
    return ok
}

Try / catch

if err := st.ExtendBlobRetention(ctx, id, opts); err != nil {
    if errors.Is(err, blob.ErrUnsupportedObjectLock) {
        log.Warn("retention extension unsupported, skipping")
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling ExtendBlobRetention on a storage provider whose implementation does not support Object Lock (e.g. the default provider, or non-S3 providers without object-lock capability).

Common situations: Users attempting to set/extend retention on repositories stored in filesystem, SFTP, or S3 buckets without object locking enabled.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at repo/blob/storage.go:46

// ErrInvalidCredentials is returned when the token used for
// authenticating with a storage provider has expired.
var ErrInvalidCredentials = errors.Errorf(InvalidCredentialsErrStr)

// ErrBlobAlreadyExists is returned when attempting to put a blob that already exists.
var ErrBlobAlreadyExists = errors.New("blob already exists")

// ErrUnsupportedPutBlobOption is returned when a PutBlob option that is not supported
// by an implementation of Storage is specified in a PutBlob call.
var ErrUnsupportedPutBlobOption = errors.New("unsupported put-blob option")

// ErrNotAVolume is returned when attempting to use a Volume method against a storage
// implementation that does not support the intended functionality.
var ErrNotAVolume = errors.New("unsupported method, storage is not a volume")

// ErrUnsupportedObjectLock is returned when attempting to use an Object Lock specific
// function on a storage implementation that does not have the intended functionality.
var ErrUnsupportedObjectLock = errors.New("object locking unsupported")

// ApplicationID is sent to storage providers as metadata in the User-Agent of requests.
// It is used to identify the application making the request.
var ApplicationID = "kopia"

// Bytes encapsulates a sequence of bytes, possibly stored in a non-contiguous buffers,
// which can be written sequentially or treated as a io.Reader.
type Bytes interface {
	io.WriterTo

	Length() int
	Reader() io.ReadSeekCloser
}

// OutputBuffer is implemented by *gather.WriteBuffer.
type OutputBuffer interface {
	io.Writer

View on GitHub (pinned to 82495e54b5)