vitessio/vitess · error

AddFile cannot be called on read-only backup

Error message

AddFile cannot be called on read-only backup

What it means

GCSBackupHandle.AddFile rejects writes to a backup handle opened read-only. Read-only handles represent backups opened for inspection or restore; adding files to them is not permitted and returns this error. It enforces that backup contents are immutable once a backup is finalized.

Source

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

	name     string
	readOnly bool
	mysqlctlerrors.PerFileErrorRecorder
}

// Directory implements BackupHandle.
func (bh *GCSBackupHandle) Directory() string {
	return bh.dir
}

// Name implements BackupHandle.
func (bh *GCSBackupHandle) Name() string {
	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 {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use a read-write handle (StartBackup/CreateBackup path) when you need AddFile
  2. Do not call AddFile on existing backups; create a new backup instead of mutating a finished one
  3. Check the handle's readOnly flag before branching into write calls

Example fix

// before
bh := bs.StartBackup(ctx, dir, name, true /* readOnly */)
w, err := bh.AddFile(ctx, "binlog.pos", 64) // error
// after
bh := bs.StartBackup(ctx, dir, name, false)
w, err := bh.AddFile(ctx, "binlog.pos", 64)
Defensive patterns

Strategy: validation

Validate before calling

if bh.readOnly {
    return errors.New("cannot AddFile on a read-only GCS backup handle")
}
w, err := bh.AddFile(ctx, filename, filesize)

Try / catch

w, err := bh.AddFile(ctx, filename, size)
if err != nil {
    if strings.Contains(err.Error(), "AddFile cannot be called on read-only backup") {
        return fmt.Errorf("%w: reopen the backup for writing", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddFile on a GCSBackupHandle created with readOnly=true (typical of restore/verification flows that open existing GCS backups read-only).

Common situations: Backup-verification or restore code accidentally invoking the write path; sharing a handle between a read pass and a write pass; copying logic written for a fresh-backup handle onto a read-only handle.

Related errors


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