vitessio/vitess · error
AbortBackup cannot be called on read-only backup
Error message
AbortBackup cannot be called on read-only backup
What it means
S3BackupHandle.AbortBackup deletes the backup (RemoveBackup) but is only valid on a handle opened for writing. If the handle was obtained in read-only mode (e.g. via StartBackup's read path or a handle opened to restore/read), the guard in s3.go:326 rejects the call outright because aborting would destroy a backup another process may be reading.
Source
Thrown at go/vt/mysqlctl/s3backupstorage/s3.go:326
// Wait is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) Wait() {
bh.waitGroup.Wait()
}
// EndBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) EndBackup(ctx context.Context) error {
if bh.readOnly {
return errors.New("EndBackup cannot be called on read-only backup")
}
bh.Wait()
return bh.Error()
}
// AbortBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) 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 is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) 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)
sendStats := bh.bs.params.Stats.Scope(stats.Operation("AWS:Request:Send"))
out, err := (&timedS3Client{client: bh.s3Client, sendStats: sendStats}).GetObject(ctx, &s3.GetObjectInput{
Bucket: &bucket,
Key: &object,
SSECustomerAlgorithm: bh.bs.s3SSE.customerAlg,
SSECustomerKey: bh.bs.s3SSE.customerKey,
SSECustomerKeyMD5: bh.bs.s3SSE.customerMd5,
})View on GitHub (pinned to 01a25a7d17)
Solutions
- Only call AbortBackup on handles returned from StartBackup (write mode)
- For read-only handles, just call Close() and use RemoveBackup on the storage engine if deletion is truly intended
- Re-open the handle in write mode via the storage engine's API if you legitimately own the backup and need to abort
- Check bh.readOnly (or your own tracking) before dispatching to AbortBackup
Example fix
// before
handle, _ := bs.StartBackup(ctx, dir, name)
_ = handle.AbortBackup(ctx) // wrong handle mode
// after
if !handle.ReadOnly() {
_ = handle.AbortBackup(ctx)
} else {
_ = handle.Close()
} Defensive patterns
Strategy: validation
Validate before calling
if bh.ReadOnly() {
// do not abort; close instead
return bh.Close()
}
return bh.AbortBackup(ctx) Type guard
func isWritableHandle(bh backupstorage.BackupHandle) bool {
h, ok := bh.(*s3backupstorage.S3BackupHandle)
return ok && !h.ReadOnly()
} Try / catch
if err := bh.AbortBackup(ctx); err != nil {
if strings.Contains(err.Error(), "cannot be called on read-only backup") {
err = bh.Close() // correct operation for read-only handles
}
return err
} Prevention
- Track handle mode (read vs write) in your backup tooling and branch on it
- Only call AbortBackup on handles obtained from StartBackup
- Prefer Close() for read handles; reserve RemoveBackup for deliberate deletion
- Add unit tests asserting handle-mode guards
When it happens
Trigger: Calling AbortBackup on an *S3BackupHandle that was created with readOnly=true — e.g. grabbing a backup handle to inspect/read contents and then attempting to abort/clean it up instead of just closing it.
Common situations: Cleanup logic written against write-mode handles being reused for read-mode handles; a tool that lists backups, opens them read-only, then tries to abort partially-uploaded ones; confusing backupstorage.BackupHandle semantics between reader and writer roles.
Related errors
- ReadFile cannot be called on read-write backup
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
- ReadFile cannot be called on read-write backup
- AddFile cannot be called on read-only backup
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/706dac666479f0b0.
Report an issue: GitHub.