vitessio/vitess · error
EndBackup cannot be called on read-only backup
Error message
EndBackup cannot be called on read-only backup
What it means
GCSBackupHandle.EndBackup refuses to finalize a backup that was opened read-only. EndBackup is the completion step of the write lifecycle and is only meaningful for read-write handles; read-only handles have nothing to finalize, so the call is rejected. This protects finished backups from lifecycle mutations.
Source
Thrown at go/vt/mysqlctl/gcsbackupstorage/gcs.go:98
return bh.name
}
// AddFile implements BackupHandle.
func (bh *GCSBackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) {
if bh.readOnly {
return nil, errors.New("AddFile cannot be called on read-only backup")
}
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)View on GitHub (pinned to 01a25a7d17)
Solutions
- Only call EndBackup on handles produced by the backup-creation (read-write) path
- Use AbortBackup-free cleanup for read-only handles — simply close files and drop the handle; there is nothing to abort or finalize
- Guard cleanup code with a readOnly check before invoking EndBackup
Example fix
// before bh := bs.StartBackup(ctx, dir, name, true /* readOnly */) err := bh.EndBackup(ctx) // error // after bh := bs.StartBackup(ctx, dir, name, false) err := bh.EndBackup(ctx) // valid for read-write handles
Defensive patterns
Strategy: validation
Validate before calling
if bh.readOnly {
return nil // nothing to finalize on a read-only handle
}
err := bh.EndBackup(ctx) Try / catch
if err := bh.EndBackup(ctx); err != nil {
if strings.Contains(err.Error(), "EndBackup cannot be called on read-only backup") {
return nil // expected for read-only handles; skip finalization
}
return err
} Prevention
- Skip EndBackup/AbortBackup in cleanup paths for read-only handles
- Structure cleanup as: close files; EndBackup/Abort only if the handle was created for writing
When it happens
Trigger: Calling EndBackup on a GCSBackupHandle created with readOnly=true (opened for restore, listing, or verification).
Common situations: Code with a generic finally/cleanup block that calls EndBackup for every handle regardless of mode; error paths in restore code that mirror backup-creation cleanup logic.
Related errors
- AddFile cannot be called on read-only backup
- AbortBackup 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/ac415c0a580d58c7.
Report an issue: GitHub.