{"record":{"id":"3ba25528883adc93","repo":"kubernetes/kops","slug":"not-a-directory-q","errorCode":null,"errorMessage":"not a directory: %q","messagePattern":"not a directory: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/pkg/vfs/sshfs.go","lineNumber":153,"sourceCode":"}\n\nfunc (p *SSHPath) Join(relativePath ...string) Path {\n\targs := []string{p.path}\n\targs = append(args, relativePath...)\n\tjoined := path.Join(args...)\n\treturn NewSSHPath(p.client, p.server, joined, p.sudo)\n}\n\nfunc mkdirAll(sftpClient *sftp.Client, dir string) error {\n\tif dir == \"/\" {\n\t\t// Must always exist\n\t\treturn nil\n\t}\n\n\tstat, err := sftpClient.Lstat(dir)\n\tif err == nil {\n\t\tif !stat.IsDir() {\n\t\t\treturn fmt.Errorf(\"not a directory: %q\", dir)\n\t\t}\n\t\treturn nil\n\t}\n\n\tparent := path.Dir(dir)\n\terr = mkdirAll(sftpClient, parent)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = sftpClient.Mkdir(dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating directory %q over sftp: %w\", dir, err)\n\t}\n\treturn nil\n}\n\nfunc (p *SSHPath) WriteFile(ctx context.Context, data io.ReadSeeker, acl ACL) error {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/util/pkg/vfs/sshfs.go#L135-L171","documentation":"mkdirAll verifies that each path component exists and is a directory before writing a file over SFTP. When Lstat succeeds for `dir` but the entry is a regular file, symlink-to-file, or other non-directory, the function aborts with this error instead of silently proceeding. It exists to give a clear message rather than confusing Mkdir/Create failures downstream.","triggerScenarios":"Calling WriteFile (via CreateFile) on an SSHPath whose parent path component is an existing non-directory, e.g. writing to /etc/kubernetes/config where /etc/kubernetes is a regular file on the remote host.","commonSituations":"A previously created config file now collides with a directory kOps needs (e.g. state store path conflicts); a stray file was created where a directory was expected; symlink pointing at a file; wrong SSHPath prefix built by the caller.","solutions":["SSH to the remote host and inspect the offending path (`ls -la <dir>`); remove or rename the file that occupies the directory name (`mv <dir> <dir>.bak` or `rm <dir>`).","Recreate the path as a directory: `mkdir -p <dir>` on the remote host, then retry the kOps operation.","Fix the SSHPath construction in your tooling so the target parent path points at an actual directory.","If a symlink is involved, repoint it (`ln -sfn <realdir> <dir>`) so it resolves to a directory."],"exampleFix":"// before (remote host has a file where a directory is needed)\n$ ls -la /etc/kubernetes\n-rw-r--r-- 1 root root 12 config\n// after\n$ rm /etc/kubernetes/config && mkdir -p /etc/kubernetes/config","handlingStrategy":"validation","validationCode":"// check remote parent path is a directory before writing\nsftp, err := newSFTPClient(ctx)\nif err != nil { return err }\nif stat, err := sftp.Lstat(dir); err == nil && !stat.IsDir() {\n    return fmt.Errorf(\"precheck: %q exists and is not a directory\", dir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Before provisioning, verify no file occupies paths you will use as directories (`ls -la` over SSH).","Keep node paths (/etc/kubernetes/...) free of ad-hoc files created by other tooling.","Build SSHPath targets programmatically from a single source of truth to avoid path typos."],"tags":["ssh","sftp","filesystem","path-conflict"],"backgroundTag":"sftp-path-is-not-a-directory","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}