vitessio/vitess · error
ReadFile cannot be called on read-write backup
Error message
ReadFile cannot be called on read-write backup
What it means
FileBackupHandle.ReadFile refuses to operate on a backup handle that was opened in read-write mode. Read streaming from a backup is reserved for read-only handles; calling ReadFile on a read-write handle is treated as a programming error by the caller. The guard exists because read-write handles are meant for writing files during backup creation, not reading them back.
Source
Thrown at go/vt/mysqlctl/filebackupstorage/file.go:134
func (fbh *FileBackupHandle) EndBackup(ctx context.Context) error {
if fbh.readOnly {
return errors.New("EndBackup cannot be called on read-only backup")
}
return nil
}
// AbortBackup is part of the BackupHandle interface
func (fbh *FileBackupHandle) AbortBackup(ctx context.Context) error {
if fbh.readOnly {
return errors.New("AbortBackup cannot be called on read-only backup")
}
return fbh.fbs.RemoveBackup(ctx, fbh.dir, fbh.name)
}
// ReadFile is part of the BackupHandle interface
func (fbh *FileBackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) {
if !fbh.readOnly {
return nil, errors.New("ReadFile cannot be called on read-write backup")
}
p, err := fileutil.SafePathJoin(FileBackupStorageRoot, fbh.dir, fbh.name, filename)
if err != nil {
return nil, err
}
f, err := os.Open(p)
if err != nil {
return nil, err
}
stat := fbh.fbs.params.Stats.Scope(stats.Operation("File:Read"))
return ioutil.NewMeteredReadCloser(f, stat.TimedIncrementBytes), nil
}
// FileBackupStorage implements BackupStorage for local file system.
type FileBackupStorage struct {
params backupstorage.Params
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Open the backup handle in read-only mode before calling ReadFile
- If the goal is to read an existing backup, use the read-only open path (e.g. ListBackups + read-only handle) instead of the write path
- Restructure the code to use separate read-only and read-write handles for read and write phases
Example fix
// before bh := fbs.StartBackup(ctx, dir, name) r, err := bh.ReadFile(ctx, "file") // error: read-write handle // after bh := fbs.AddBackup(ctx, dir, name, true /* readOnly */) r, err := bh.ReadFile(ctx, "file")
Defensive patterns
Strategy: validation
Validate before calling
if !bh.readOnly {
return nil, errors.New("need a read-only handle to call ReadFile")
}
r, err := bh.ReadFile(ctx, filename) Type guard
func isReadOnlyFileHandle(bh mysqlctl.BackupHandle) bool {
fbh, ok := bh.(*filebackupstorage.FileBackupHandle)
return ok && fbh.ReadOnly()
} Try / catch
r, err := bh.ReadFile(ctx, filename)
if err != nil {
if strings.Contains(err.Error(), "ReadFile cannot be called on read-write backup") {
// re-open read-only and retry
}
return err
} Prevention
- Always open handles read-only for any read/restore path
- Keep read and write phases on separate handles
- Audit call sites that mix AddFile/ReadFile on the same handle
When it happens
Trigger: Calling ReadFile on a FileBackupHandle obtained without the readOnly option (e.g. via StartBackup or AddBackup with readOnly=false), while a read-only open would have succeeded.
Common situations: Tooling that reuses one handle for both reading and writing backup files; code that forgets to request a read-only handle when scanning existing backup contents; refactors that changed a handle's mode but not its call sites.
Related errors
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
- AbortBackup cannot be called on read-only 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/c440662e69408818.
Report an issue: GitHub.