vitessio/vitess · error
AbortBackup cannot be called on read-only backup
Error message
AbortBackup cannot be called on read-only backup
What it means
GCSBackupHandle.AbortBackup refuses to abort a read-only handle. AbortBackup deletes the backup via RemoveBackup, and read-only handles exist precisely to prevent any mutation of the stored backup, so aborting is denied. A read-only consumer cannot and should not delete a backup it is only reading.
Source
Thrown at go/vt/mysqlctl/gcsbackupstorage/gcs.go:106
object := objName(bh.dir, bh.name, filename)
return bh.client.Bucket(bucket).Object(object).NewWriter(ctx), nil
}
// Wait implements BackupHandle.
func (bh *GCSBackupHandle) Wait() {}
// EndBackup implements BackupHandle.
func (bh *GCSBackupHandle) EndBackup(ctx context.Context) error {
if bh.readOnly {
return errors.New("EndBackup cannot be called on read-only backup")
}
return nil
}
// AbortBackup implements BackupHandle.
func (bh *GCSBackupHandle) AbortBackup(ctx context.Context) error {
if bh.readOnly {
return errors.New("AbortBackup cannot be called on read-only backup")
}
return bh.bs.RemoveBackup(ctx, bh.dir, bh.name)
}
// ReadFile implements BackupHandle.
func (bh *GCSBackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) {
if !bh.readOnly {
return nil, errors.New("ReadFile cannot be called on read-write backup")
}
object := objName(bh.dir, bh.name, filename)
return bh.client.Bucket(bucket).Object(object).NewReader(ctx)
}
// GCSBackupStorage implements BackupStorage for Google Cloud Storage.
type GCSBackupStorage struct {
// client is the instance of the Google Cloud Storage Go client.
// Once this field is set, it must not be written again/unset to nil.
_client *storage.ClientView on GitHub (pinned to 01a25a7d17)
Solutions
- Never call AbortBackup on read-only handles; to delete a backup use the storage-level RemoveBackup with a read-write-capable context and permissions
- In shared cleanup paths, check readOnly before choosing between AbortBackup and plain close
- If backup deletion is intended, obtain the handle via the write/management API instead of the read-only open
Example fix
// before
bh := bs.StartBackup(ctx, dir, name, true /* readOnly */)
err := bh.AbortBackup(ctx) // error
// after
if !bh.readOnly {
err := bh.AbortBackup(ctx)
} else {
// nothing to abort; just close open files
} Defensive patterns
Strategy: validation
Validate before calling
if bh.readOnly {
return errors.New("cannot AbortBackup on a read-only handle; use RemoveBackup with proper permissions if deletion is intended")
}
err := bh.AbortBackup(ctx) Try / catch
if err := bh.AbortBackup(ctx); err != nil {
if strings.Contains(err.Error(), "AbortBackup cannot be called on read-only backup") {
return nil // nothing to abort
}
return err
} Prevention
- Guard shared error-path cleanup with a readOnly check
- Use storage-level RemoveBackup (with write permissions) when deletion is truly intended
- Never reuse writer cleanup logic verbatim in restore code
When it happens
Trigger: Calling AbortBackup on a GCSBackupHandle opened with readOnly=true, typically in a cleanup/error path shared with the backup-creation flow.
Common situations: Restore or verification code reusing a 'defer bh.AbortBackup(ctx)' pattern written for the writer side; permission-restricted tooling that opens read-only but tries to clean up on failure.
Related errors
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
- ReadFile cannot be called on read-write backup
- ReadFile cannot be called on read-write backup
- lock already acquired
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ffb8be74a492311f.
Report an issue: GitHub.