vitessio/vitess · error
ReadFile cannot be called on read-write backup
Error message
ReadFile cannot be called on read-write backup
What it means
S3BackupHandle.ReadFile downloads an object from the backup and is only permitted on read-only handles. On a handle opened for writing (from StartBackup), reading is disallowed because the write session's semantics don't guarantee the object exists or is final, so the guard at s3.go:334 fails fast.
Source
Thrown at go/vt/mysqlctl/s3backupstorage/s3.go:334
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,
})
if err != nil {
return nil, err
}
return out.Body, nil
}
var _ backupstorage.BackupHandle = (*S3BackupHandle)(nil)
View on GitHub (pinned to 01a25a7d17)
Solutions
- Open a read-only handle for the existing backup and call ReadFile on that
- Use AddFile/Close on write handles; reserve ReadFile for read handles
- Restructure code so backup writing and manifest reading use separate handles
- Assert readOnly before calling ReadFile
Example fix
// before bh, _ := bs.StartBackup(ctx, dir, name) r, err := bh.ReadFile(ctx, "manifest.json") // after bh, _ := bs.StartBackup(ctx, dir, name) // ... write files, then use a read handle for verification rbh, _ := bs.StartBackup(ctx, dir, name) // or storage-engine read API with readOnly r, err := rbh.ReadFile(ctx, "manifest.json")
Defensive patterns
Strategy: validation
Validate before calling
if !bh.ReadOnly() {
return fmt.Errorf("ReadFile requires a read-only handle")
}
r, err := bh.ReadFile(ctx, filename) Type guard
func canRead(bh backupstorage.BackupHandle) bool {
h, ok := bh.(*s3backupstorage.S3BackupHandle)
return ok && h.ReadOnly()
} Try / catch
rc, err := bh.ReadFile(ctx, filename)
if err != nil && strings.Contains(err.Error(), "cannot be called on read-write backup") {
return fmt.Errorf("use a read handle to read %s: %w", filename, err)
} Prevention
- Keep writer handles for AddFile only; open separate read handles for verification
- Name variables to encode handle mode (wbh vs rbh)
- Review backup vs restore code paths for handle crossover
- Wrap handle usage in small helpers that encode the mode in the API
When it happens
Trigger: Calling ReadFile on the handle returned by StartBackup (read-write mode) instead of on a handle opened for reading an existing backup.
Common situations: Restore tooling that accidentally uses the writer handle; code that starts a backup, then tries to re-read the manifest from the same handle; copy/pasted handle usage between backup and restore paths.
Related errors
- AbortBackup cannot be called on read-only 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/7f680dc02e19ccb8.
Report an issue: GitHub.