{"record":{"id":"627b85e0363ef790","repo":"kubernetes/kops","slug":"error-creating-directory-q-over-sftp-w","errorCode":null,"errorMessage":"error creating directory %q over sftp: %w","messagePattern":"error creating directory %q over sftp: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/pkg/vfs/sshfs.go","lineNumber":166,"sourceCode":"\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 {\n\tsftpClient, err := p.newClient(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer sftpClient.Close()\n\n\tdir := path.Dir(p.path)\n\terr = mkdirAll(sftpClient, dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttempfile := path.Join(dir, fmt.Sprintf(\".tmp-%d\", rand.Int63()))","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/util/pkg/vfs/sshfs.go#L148-L184","documentation":"After ensuring the parent directory exists, mkdirAll attempts sftpClient.Mkdir(dir) to create the leaf directory. If the SFTP server rejects the Mkdir, the underlying error is wrapped with this message. Common underlying causes are EPERM/EACCES (no write permission on the parent) or the directory appearing concurrently.","triggerScenarios":"WriteFile -> mkdirAll -> sftpClient.Mkdir fails because the SSH user lacks write permission on the parent directory, the filesystem is read-only, or a race created the directory between Lstat and Mkdir (EEXIST).","commonSituations":"Deploying to a host where the SSH user is not root and lacks sudo (sudo only helps the shell `mv` path, not SFTP); read-only root filesystem on a node; disk full; SELinux/AppArmor denying the SFTP subsystem write.","solutions":["Check permissions of the parent directory on the remote host and grant write access (`chmod`/`chown`) or run kOps as a user with sufficient rights.","If permission is the issue and the command supports it, target a path the SSH user can write, or pre-create the directory as root via SSH: `sudo mkdir -p <dir>`.","Check for EEXIST in the wrapped error — if the directory was created concurrently, simply retry the operation.","Verify the remote filesystem is writable (not read-only, not full): `touch <dir>/.write-test` over SSH."],"exampleFix":"// before: sftp user cannot create /etc/kubernetes/manifests\nsshfs.NewSSHPath(client, server, \"/etc/kubernetes/manifests/file\", false)\n// after: pre-create with elevated rights, then write over sftp\n$ ssh host 'sudo mkdir -p /etc/kubernetes/manifests && sudo chown sftpuser /etc/kubernetes/manifests'","handlingStrategy":"retry","validationCode":"// ensure the SSH user can create directories under the parent\nsession := sshRun(host, \"test -w $(dirname /etc/kubernetes/manifests) && echo writable\")\n// expect output \"writable\"","typeGuard":null,"tryCatchPattern":"err := path.WriteFile(ctx, data, acl)\nif err != nil && strings.Contains(err.Error(), \"error creating directory\") {\n    var sftpErr *sftp.StatusError\n    if errors.As(err, &sftpErr) && sftpErr.Code == uint32(sftp.ErrSSHFxPermissionDenied) {\n        return fmt.Errorf(\"fix remote permissions or pre-create dir: %w\", err)\n    }\n}","preventionTips":["Provision the SFTP/SSH user with write access to all target directories up front.","Pre-create directory trees with correct ownership in your provisioning pipeline.","Treat EEXIST as benign and retry once on concurrent-create races."],"tags":["ssh","sftp","permissions","mkdir"],"backgroundTag":"sftp-mkdir-permission-denied","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"}