{"record":{"id":"dfab81dce61995c6","repo":"kubernetes/kops","slug":"error-creating-sftp-client-at-stdin-pipe-w","errorCode":null,"errorMessage":"error creating sftp client (at stdin pipe): %w","messagePattern":"error creating sftp client \\(at stdin pipe\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/pkg/vfs/sshfs.go","lineNumber":72,"sourceCode":"}\n\nfunc (p *SSHPath) newClient(ctx context.Context) (*sftp.Client, error) {\n\tif !p.sudo {\n\t\tsftpClient, err := sftp.NewClient(p.client)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error creating sftp client: %w\", err)\n\t\t}\n\n\t\treturn sftpClient, nil\n\t}\n\ts, err := p.client.NewSession()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating sftp client (in new-session): %w\", err)\n\t}\n\n\tstdin, err := s.StdinPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating sftp client (at stdin pipe): %w\", err)\n\t}\n\tstdout, err := s.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating sftp client (at stdout pipe): %w\", err)\n\t}\n\n\terr = s.Start(\"sudo /usr/lib/openssh/sftp-server\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating sftp client (executing 'sudo /usr/lib/openssh/sftp-server'): %w\", err)\n\t}\n\n\tc, err := sftp.NewClientPipe(stdout, stdin)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error starting sftp (executing 'sudo /usr/lib/openssh/sftp-server'): %w\", err)\n\t}\n\treturn c, nil\n}\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/util/pkg/vfs/sshfs.go#L54-L90","documentation":"SSHPath.newClient in util/pkg/vfs/sshfs.go wraps errors from s.StdinPipe() with this message on the sudo=true path. After opening an SSH session, pipes for stdin/stdout are created to talk to the remote sftp-server process; a failure here means the session could not provide the stdin pipe, indicating the session channel is in a bad or already-closed state.","triggerScenarios":"SSHPath operations with sudo=true where s.StdinPipe() returns an error after NewSession() succeeded — typically when the underlying ssh connection was closed between opening the session and requesting the pipe, or the session was rejected/reaped immediately by the server.","commonSituations":"Race with connection teardown (server closed the connection right after session setup); broken ssh.Client sharing across goroutines leading to closed channels; server abruptly closing the channel due to policy limits.","solutions":["Re-establish the ssh.Client and retry the operation — the connection was likely closed mid-setup","Avoid sharing one ssh.Client across concurrent goroutines without synchronization","Enable SSH keepalives to keep the connection alive between VFS operations","Check sshd server logs for why the channel was torn down (e.g. session limits)"],"exampleFix":"// before\nclient, err := ssh.Dial(\"tcp\", host, cfg) // reused long after dial\n// after\n// keep connection alive and re-dial on failure:\nclient, err := ssh.Dial(\"tcp\", host, cfg)\nif err != nil {\n\treturn nil, fmt.Errorf(\"re-establishing ssh connection: %w\", err)\n}\n// set ClientConfig.HostKeyCallback/Timeout and a KeepAlive via client.SendRequest loop","handlingStrategy":"retry","validationCode":"// verify the connection is alive right before VFS operations\n_, err := client.SendRequest(\"keepalive@openssh.com\", true, nil)\nif err != nil {\n\tclient, err = ssh.Dial(\"tcp\", host, cfg) // re-dial stale connection\n\tif err != nil { log.Fatalf(\"ssh connection dead: %v\", err) }\n}","typeGuard":"func isStdinPipeError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"sftp client (at stdin pipe)\")\n}","tryCatchPattern":"err := p.ReadDir(ctx)\nif err != nil && strings.Contains(err.Error(), \"error creating sftp client (at stdin pipe)\") {\n\t// connection died mid-setup: rebuild and retry once\n\tclient, derr := ssh.Dial(\"tcp\", host, cfg)\n\tif derr == nil {\n\t\tp = vfs.NewSSHPath(client, host, path, true)\n\t\treturn p.ReadDir(ctx)\n\t}\n}\nreturn err","preventionTips":["Send SSH keepalive requests periodically to keep the connection healthy","Re-dial the ssh.Client at the first sign of channel/pipe errors","Avoid concurrent use of a single ssh.Client from multiple goroutines","Minimize time between dialing and performing VFS operations","Check sshd logs for abrupt channel teardowns on the target host"],"tags":["ssh","sftp","vfs","network"],"backgroundTag":"ssh-session-open-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}