{"record":{"id":"d8f7508cb1cc3425","repo":"k3s-io/k3s","slug":"s-directory-is-empty","errorCode":null,"errorMessage":"%s directory is empty","messagePattern":"(.+?) directory is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/cluster/bootstrap.go","lineNumber":194,"sourceCode":"\t)\n\n\tfor _, dir := range bootstrapDirs {\n\t\td := filepath.Join(c.config.DataDir, dir)\n\t\tif _, err := os.Stat(d); os.IsNotExist(err) {\n\t\t\terrMsg := fmt.Sprintf(missingDir, d)\n\t\t\tlogrus.Debug(errMsg)\n\t\t\treturn errors.New(errMsg)\n\t\t}\n\n\t\tok, err := isDirEmpty(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif ok {\n\t\t\terrMsg := fmt.Sprintf(emptyDir, d)\n\t\t\tlogrus.Debug(errMsg)\n\t\t\treturn errors.New(errMsg)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// migrateBootstrapData migrates bootstrap data from the old format to the new format.\nfunc migrateBootstrapData(ctx context.Context, data io.Reader, files bootstrap.PathsDataformat) error {\n\tlogrus.Info(\"Migrating bootstrap data to new format\")\n\n\tvar oldBootstrapData map[string][]byte\n\tif err := json.NewDecoder(data).Decode(&oldBootstrapData); err != nil {\n\t\t// if this errors here, we can assume that the error being thrown\n\t\t// is not related to needing to perform a migration.\n\t\treturn err\n\t}\n\n\t// iterate through the old bootstrap data structure","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/cluster/bootstrap.go#L176-L212","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: tls/ exists but empty\nls /var/lib/rancher/k3s/server/tls   # (no output)\n\n# after: contents restored from backup\nrsync -a backup:/k3s-server/tls/ /var/lib/rancher/k3s/server/tls/","handlingStrategy":"validation","validationCode":"// preflight: PKI directories are non-empty\nfor _, d := range []string{\"cred\", \"tls\", \"tls/etcd\"} {\n    p := filepath.Join(dataDir, d)\n    entries, err := os.ReadDir(p)\n    if err != nil || len(entries) == 0 {\n        return fmt.Errorf(\"%s directory is empty\", p)\n    }\n}","typeGuard":"func serverPKIDirsPopulated(dataDir string) bool {\n    for _, d := range []string{\"cred\", \"tls\", \"tls/etcd\"} {\n        entries, err := os.ReadDir(filepath.Join(dataDir, d))\n        if err != nil || len(entries) == 0 {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["bootstrap","certificates","data-dir","recovery"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}