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

  1. Open the backup handle in read-only mode before calling ReadFile
  2. 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
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/c440662e69408818. Report an issue: GitHub.