{"record":{"id":"b24e1ca645502ba0","repo":"k3s-io/k3s","slug":"etcd-snapshot-path-must-be-a-file-not-a-director","errorCode":null,"errorMessage":"etcd: snapshot path must be a file, not a directory: %s","messagePattern":"etcd: snapshot path must be a file, not a directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/etcd/etcd.go","lineNumber":416,"sourceCode":"\t\t\t}\n\t\t\tdir, err := snapshotDir(e.config, true)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithMessage(err, \"failed to get the snapshot dir\")\n\t\t\t}\n\t\t\tpath, err := s3client.Download(ctx, e.config.ClusterResetRestorePath, dir)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithMessage(err, \"failed to download snapshot from S3\")\n\t\t\t}\n\t\t\te.config.ClusterResetRestorePath = path\n\t\t\tlogrus.Infof(\"S3 download complete for %s\", e.config.ClusterResetRestorePath)\n\t\t}\n\n\t\tinfo, err := os.Stat(e.config.ClusterResetRestorePath)\n\t\tif os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"etcd: snapshot path does not exist: %s\", e.config.ClusterResetRestorePath)\n\t\t}\n\t\tif info.IsDir() {\n\t\t\treturn fmt.Errorf(\"etcd: snapshot path must be a file, not a directory: %s\", e.config.ClusterResetRestorePath)\n\t\t}\n\t\tif err := e.Restore(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := e.setName(true); err != nil {\n\t\treturn err\n\t}\n\t// touch a file to avoid multiple resets\n\tif err := os.WriteFile(e.ResetFile(), []byte{}, 0600); err != nil {\n\t\treturn err\n\t}\n\n\treturn e.newCluster(ctx, wg, true)\n}\n\n// Start starts the datastore","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/etcd/etcd.go#L398-L434","documentation":"The companion check to the missing-path error: the --cluster-reset-restore-path must be a regular file containing an etcd db snapshot. If os.Stat succeeds and reports a directory, restore aborts before touching the datastore - a directory of snapshots is not a restorable artifact.","triggerScenarios":"Passing a directory to --cluster-reset-restore-path, most commonly the snapshots directory itself or an S3 prefix that resolved to a directory (pkg/etcd/etcd.go:415-417).","commonSituations":"Operator passes /var/lib/rancher/k3s/server/db/snapshots instead of a specific .db file; unzipping/copying a snapshot into a folder and pointing at the folder; confusion between the S3 bucket path and the object key.","solutions":["Point at a specific snapshot file, e.g. /var/lib/rancher/k3s/server/db/snapshots/<name>.db - find one with `k3s etcd-snapshot ls` or `ls`.","If the snapshot arrived as a directory (e.g. extracted from an archive), locate the .db file inside it and pass that path.","For S3 restores, pass the object path; ensure the configured S3 location actually contains the snapshot object."],"exampleFix":"# before\nk3s server --cluster-reset --cluster-reset-restore-path=/var/lib/rancher/k3s/server/db/snapshots\n\n# after\nk3s server --cluster-reset --cluster-reset-restore-path=/var/lib/rancher/k3s/server/db/snapshots/etcd-snapshot-1723680000-0.db","handlingStrategy":"validation","validationCode":"// Require a regular file, not a directory:\ninfo, err := os.Stat(snapshotPath)\nif err != nil { log.Fatal(err) }\nif info.IsDir() {\n    log.Fatalf(\"%s is a directory; pick the snapshot file inside it\", snapshotPath)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use `k3s etcd-snapshot ls` output to construct restore paths exactly.","For S3 restores, configure S3 options and pass the object path, not the bucket/prefix.","Codify restore steps in a tested runbook script."],"tags":["etcd","snapshot","restore","cluster-reset","k3s"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}