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 structureView on GitHub (pinned to 6ba341e396)
Solutions
- Restore the directory contents from a known-good full data-dir backup
- If no valid backup exists, remove the whole data-dir and re-initialize or re-join the node (new certs will be issued)
- 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
- Test restores end-to-end: directory skeleton without contents fails this check
- Use rsync -a (contents, not just structure) in restore tooling
- After uninstall/reset, remove the full data-dir so no empty skeletons survive
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
- missing %s directory from ${data-dir}
- --token is required
- service %s is not recognized
- cannot perform cluster-reset while server URL is set - remov
- does not exist, please pass --token to complete the restora
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/d8f7508cb1cc3425.
Report an issue: GitHub.