vitessio/vitess · error

ReadFile cannot be called on read-write backup

Error message

ReadFile cannot be called on read-write backup

What it means

GCSBackupHandle.ReadFile requires a read-only handle; it is the mirror guard of AddFile's. Reads are only permitted on handles opened read-only so that restore/inspection traffic can be distinguished from backup writing, and a read-write handle cannot be used to stream files back.

Source

Thrown at go/vt/mysqlctl/gcsbackupstorage/gcs.go:114

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.Client
	// mu guards all fields.
	mu sync.Mutex
}

// ListBackups implements BackupStorage.
func (bs *GCSBackupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) {
	c, err := bs.client(ctx)
	if err != nil {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Open a second handle with readOnly=true for any ReadFile calls
  2. Complete the write phase, then re-open the backup read-only to verify contents
  3. Check the handle's readOnly mode before dispatching read vs write operations

Example fix

// before
bh := bs.StartBackup(ctx, dir, name, false)
r, err := bh.ReadFile(ctx, "backupinfo") // error
// after
rh := bs.StartBackup(ctx, dir, name, true /* readOnly */)
r, err := rh.ReadFile(ctx, "backupinfo")
Defensive patterns

Strategy: validation

Validate before calling

if bh.readOnly == false {
    return errors.New("ReadFile requires a read-only handle; re-open the backup read-only")
}
r, err := bh.ReadFile(ctx, filename)

Try / catch

r, err := bh.ReadFile(ctx, filename)
if err != nil {
    if strings.Contains(err.Error(), "ReadFile cannot be called on read-write backup") {
        // obtain a readOnly handle and retry
    }
    return err
}

Prevention

When it happens

Trigger: Calling ReadFile on a GCSBackupHandle created for backup writing (readOnly=false, e.g. from StartBackup with readOnly=false).

Common situations: Verification code that opens a handle to write and then tries to read back what it wrote; generic helper functions assuming one handle serves both directions.

Related errors


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