{"record":{"id":"3f8fcb4649e52d6a","repo":"kubernetes/kops","slug":"removing-destination-sftp-file-q-before-rename","errorCode":null,"errorMessage":"removing destination sftp file %q before rename: %w","messagePattern":"removing destination sftp file %q before rename: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/pkg/vfs/sshfs.go","lineNumber":240,"sourceCode":"\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"unexpected acl type %T\", acl)\n\t\t} else {\n\t\t\terr = sftpClient.Chmod(tempfile, sshACL.Mode)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error during chmod of %q: %w\", tempfile, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// posix rename will replace the destination (normal sftp rename does not)\n\tusePosixRename := true\n\tif usePosixRename {\n\t\t// posix rename fails if destination exists, try to delete just in case\n\t\tif err := sftpClient.Remove(p.path); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\t// expected when file does not exist already\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"removing destination sftp file %q before rename: %w\", p.path, err)\n\t\t\t}\n\t\t}\n\t\tif err := sftpClient.Rename(tempfile, p.path); err != nil {\n\t\t\treturn fmt.Errorf(\"renaming sftp file %q -> %q (with posix rename): %w\", tempfile, p.path, err)\n\t\t}\n\t\tdeleteTempFile = false\n\t} else {\n\t\tvar session *ssh.Session\n\t\tsession, err = p.client.NewSession()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"creating session for rename: %w\", err)\n\t\t}\n\t\tdefer session.Close()\n\n\t\tcmd := \"mv \" + tempfile + \" \" + p.path\n\t\tif p.sudo {\n\t\t\tcmd = \"sudo \" + cmd\n\t\t}","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/util/pkg/vfs/sshfs.go#L222-L258","documentation":"Because POSIX-rename via SFTP fails when the destination exists, WriteFile first removes the destination file p.path. If Remove fails with anything other than NotExist, this error wraps the cause. It means the old file could not be cleared, so the atomic rename was not attempted.","triggerScenarios":"sftpClient.Remove(p.path) fails with EACCES/EPERM (no write permission on the directory containing the destination), EBUSY, or a connection error — i.e. the destination exists but cannot be deleted.","commonSituations":"Overwriting an existing file on a host where the SFTP user lacks directory write permission (file owned by root); destination directory has sticky bit and different owner; immutable attribute (chattr +i) on the file.","solutions":["SSH to the host and remove/fix ownership of the destination: `sudo rm <path>` or `sudo chown <sftpuser> <dir>`.","Grant the SFTP user write permission on the destination directory (directory write permission, not file ownership, is what Remove needs).","Check for immutable flags: `lsattr <path>` and `sudo chattr -i <path>` if set.","Retry if the wrapped error indicates a transient connection issue."],"exampleFix":"// before: /etc/kubernetes/manifests owned by root, sftp user cannot delete\n$ ssh host 'sudo chown -R sftpuser:sftpuser /etc/kubernetes/manifests'\n// after: retry kops write — destination removal succeeds","handlingStrategy":"validation","validationCode":"// destination removal needs write permission on the directory\nout := sshRun(host, fmt.Sprintf(\"test -w %s && echo dir-writable\", path.Dir(dest)))","typeGuard":null,"tryCatchPattern":"err := path.WriteFile(ctx, data, acl)\nif err != nil {\n    var permErr *fs.PathError\n    if strings.Contains(err.Error(), \"removing destination sftp file\") {\n        // escalate ownership on the remote dir, then retry\n        sshRun(host, fmt.Sprintf(\"sudo chown %s %s\", sftpUser, path.Dir(dest)))\n        return path.WriteFile(ctx, data, acl)\n    }\n    _ = permErr\n    return err\n}","preventionTips":["Give the SFTP user write permission on directories whose files will be overwritten.","Avoid chattr +i and root-only ownership on kOps-managed files.","Stop competing writers before overwriting managed manifests."],"tags":["ssh","sftp","permissions","file-delete"],"backgroundTag":"sftp-delete-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"}