vitessio/vitess · error

unable to delete file: %v

Error message

unable to delete file: %v

What it means

removeStateFile deletes the restore state file from the tablet directory after a successful restore. If os.Remove fails for any reason (missing file, permissions, I/O error), the underlying error is wrapped with this message. It signals that leftover restore state could not be cleaned up, which may make the next restore think a previous one was interrupted.

Source

Thrown at go/vt/mysqlctl/backupengine.go:776

	// change Create to Open
	// rename func to openStateFile
	// change to return a *File
	fname := filepath.Join(cnf.TabletDir(), RestoreState)
	fd, err := os2.Create(fname)
	if err != nil {
		return fmt.Errorf("unable to create file: %v", err)
	}
	if err = fd.Close(); err != nil {
		return fmt.Errorf("unable to close file: %v", err)
	}
	return nil
}

// delete restore state file
func removeStateFile(cnf *Mycnf) error {
	fname := filepath.Join(cnf.TabletDir(), RestoreState)
	if err := os.Remove(fname); err != nil {
		return fmt.Errorf("unable to delete file: %v", err)
	}
	return nil
}

// RestoreWasInterrupted tells us whether a previous restore
// was interrupted and we are now retrying it
func RestoreWasInterrupted(cnf *Mycnf) bool {
	name := filepath.Join(cnf.TabletDir(), RestoreState)
	_, err := os.Stat(name)
	return err == nil
}

// GetBackupDir returns the directory where backups for the
// given keyspace/shard are (or will be) stored
func GetBackupDir(keyspace, shard string) string {
	return fmt.Sprintf("%v/%v", keyspace, shard)
}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check that the tablet directory is writable by the process user (ls -ld on the tablet dir).
  2. Verify whether the state file exists; if it is simply missing, treat cleanup as done (skip os.Remove when os.IsNotExist).
  3. Inspect the wrapped %v error for the concrete errno (ENOENT vs EACCES vs EIO) and fix accordingly.
  4. Rerun the restore; the state file is only leftover state, not data.

Example fix

// before
if err := os.Remove(fname); err != nil {
	return fmt.Errorf("unable to delete file: %v", err)
}
// after
if err := os.Remove(fname); err != nil && !os.IsNotExist(err) {
	return fmt.Errorf("unable to delete file %s: %w", fname, err)
}
return nil
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(fname); err == nil {
	if err := os.Remove(fname); err != nil {
		return fmt.Errorf("unable to delete file %s: %w", fname, err)
	}
}

Try / catch

if err := removeStateFile(cnf); err != nil {
	log.Warn("restore state file cleanup failed", slog.Any("error", err))
	// non-fatal: next restore will see RestoreWasInterrupted and clean up
}

Prevention

When it happens

Trigger: Called during restore cleanup in mysqlctl; os.Remove fails on <TabletDir>/<RestoreState> because the file is absent, the vitess user lacks write permission on the tablet directory, or the filesystem reports an I/O error.

Common situations: Tablet directory ownership/permissions wrong after running as different user; state file already removed by concurrent cleanup; disk full or read-only mount after a crash.

Related errors


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