k3s-io/k3s · critical

%s directory is empty

Error message

%s directory is empty

What it means

The second half of checkCertDirs: a bootstrap directory (cred, tls, or tls/etcd) exists but contains zero entries. Empty PKI directories indicate a wiped or partially restored data-dir, and k3s refuses to start rather than generate new CA material alongside existing state that references the old one.

Source

Thrown at pkg/cluster/bootstrap.go:194

	)

	for _, dir := range bootstrapDirs {
		d := filepath.Join(c.config.DataDir, dir)
		if _, err := os.Stat(d); os.IsNotExist(err) {
			errMsg := fmt.Sprintf(missingDir, d)
			logrus.Debug(errMsg)
			return errors.New(errMsg)
		}

		ok, err := isDirEmpty(d)
		if err != nil {
			return err
		}

		if ok {
			errMsg := fmt.Sprintf(emptyDir, d)
			logrus.Debug(errMsg)
			return errors.New(errMsg)
		}
	}

	return nil
}

// migrateBootstrapData migrates bootstrap data from the old format to the new format.
func migrateBootstrapData(ctx context.Context, data io.Reader, files bootstrap.PathsDataformat) error {
	logrus.Info("Migrating bootstrap data to new format")

	var oldBootstrapData map[string][]byte
	if err := json.NewDecoder(data).Decode(&oldBootstrapData); err != nil {
		// if this errors here, we can assume that the error being thrown
		// is not related to needing to perform a migration.
		return err
	}

	// iterate through the old bootstrap data structure

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Restore the directory contents from a known-good full data-dir backup
  2. If no valid backup exists, remove the whole data-dir and re-initialize or re-join the node (new certs will be issued)
  3. Fix restore tooling to copy file contents, not just the directory skeleton

Example fix

# before: tls/ exists but empty
ls /var/lib/rancher/k3s/server/tls   # (no output)

# after: contents restored from backup
rsync -a backup:/k3s-server/tls/ /var/lib/rancher/k3s/server/tls/
Defensive patterns

Strategy: validation

Validate before calling

// preflight: PKI directories are non-empty
for _, d := range []string{"cred", "tls", "tls/etcd"} {
    p := filepath.Join(dataDir, d)
    entries, err := os.ReadDir(p)
    if err != nil || len(entries) == 0 {
        return fmt.Errorf("%s directory is empty", p)
    }
}

Type guard

func serverPKIDirsPopulated(dataDir string) bool {
    for _, d := range []string{"cred", "tls", "tls/etcd"} {
        entries, err := os.ReadDir(filepath.Join(dataDir, d))
        if err != nil || len(entries) == 0 {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: data-dir where e.g. /var/lib/rancher/k3s/server/tls exists but is empty — restore scripts that recreate the directory structure without contents, or cleanup that removed files but left directories.

Common situations: Backup/restore procedures using mkdir -p plus selective copy; interrupted uninstalls; containers with data-dir on a volume that got reformatted; a previous failed bootstrap leaving skeleton dirs.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/d8f7508cb1cc3425. Report an issue: GitHub.