VictoriaMetrics/VictoriaMetrics · error

cannot perform server-side copying from %s to %s: both of th

Error message

cannot perform server-side copying from %s to %s: both of them must be AZBlob

What it means

CopyPart performs Azure Blob server-side (async) copying, which requires both the source and destination filesystems to be the azremote AZBlob FS implementation. The library type-asserts srcFS to *azremote.FS and throws this error when the source is a different storage backend (e.g. S3 or GCS), because StartCopyFromURL only works between Azure blobs.

Source

Thrown at lib/backup/azremote/azblob.go:212

	return parts, nil
}

// DeletePart deletes part p from fs.
func (fs *FS) DeletePart(p common.Part) error {
	return fs.delete(p.RemotePath(fs.Dir))
}

// RemoveEmptyDirs recursively removes empty dirs in fs.
func (fs *FS) RemoveEmptyDirs() error {
	// Blob storage has no directories, so nothing to remove.
	return nil
}

// CopyPart copies p from srcFS to fs.
func (fs *FS) CopyPart(srcFS common.OriginFS, p common.Part) error {
	src, ok := srcFS.(*FS)
	if !ok {
		return fmt.Errorf("cannot perform server-side copying from %s to %s: both of them must be AZBlob", srcFS, fs)
	}

	sbc := src.client.NewBlobClient(p.RemotePath(src.Dir))
	dbc := fs.clientForPart(p)

	// In order to support copy of files larger than 256MB, we need to use the async copy
	// Ref: https://learn.microsoft.com/en-us/rest/api/storageservices/copy-blob-from-url
	_, err := dbc.StartCopyFromURL(fs.ctx, sbc.URL(), &blob.StartCopyFromURLOptions{})
	if err != nil {
		return fmt.Errorf("cannot start async copy %q from %s to %s: %w", p.Path, src, fs, err)
	}

	var copyStatus *blob.CopyStatusType
	var copyStatusDescription *string
	for {
		r, err := dbc.GetProperties(fs.ctx, nil)
		if err != nil {
			return fmt.Errorf("failed to check copy status, cannot get properties of %q at %s: %w", p.Path, fs, err)

View on GitHub (pinned to 5079fb58f1)

Solutions

  1. Ensure both srcFS and fs are created via the AZBlob (azremote) filesystem implementation.
  2. If the source cannot be Azure Blob, download the part from the source and upload it to the destination instead of server-side copy.
  3. Check your backup/storage configuration so both origin and destination point to the same Azure storage backend type.

Example fix

// before
err := dstFS.CopyPart(s3FS, part) // s3FS is *s3remote.FS
// after
err := dstFS.CopyPart(azFS, part) // both must be *azremote.FS
Defensive patterns

Strategy: type-guard

Validate before calling

// before calling CopyPart
azFS, ok := srcFS.(*azremote.FS)
if !ok {
    return fmt.Errorf("server-side copy unsupported for %T; use download+upload fallback", srcFS)
}

Type guard

func asAZBlobFS(fs common.OriginFS) (*azremote.FS, bool) {
    f, ok := fs.(*azremote.FS)
    return f, ok
}

Try / catch

if err := dstFS.CopyPart(srcFS, part); err != nil && strings.Contains(err.Error(), "both of them must be AZBlob") {
    // fall back to DownloadPart(srcFS) + UploadPart(dstFS)
}

Prevention

When it happens

Trigger: Calling CopyPart with a srcFS that is not the AZBlob FS wrapper, e.g. mixing S3-backed and AZBlob-backed origins in a backup/restore pipeline.

Common situations: Migration jobs between cloud providers where the source is S3/GCS and the destination is Azure; misconfigured backup destination so a non-Azure FS is passed; tests or custom OriginFS implementations that aren't backed by Azure Blob.

Related errors


AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03). Data as JSON: /api/errors/8734d7b3250d6626. Report an issue: GitHub.