{"record":{"id":"412480bab61ced33","repo":"kopia/kopia","slug":"error-creating-sftp-client-pipe","errorCode":null,"errorMessage":"error creating sftp client pipe","messagePattern":"error creating sftp client pipe","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/blob/sftp/sftp_storage.go","lineNumber":504,"sourceCode":"\tif err = cmd.Start(); err != nil {\n\t\treturn nil, errors.Wrap(err, \"error starting SSH\")\n\t}\n\n\tcloseFunc := func() error {\n\t\tp := cmd.Process\n\t\tif p != nil {\n\t\t\tp.Kill() //nolint:errcheck\n\t\t}\n\n\t\treturn nil\n\t}\n\n\t// open the SFTP session\n\tc, err := sftp.NewClientPipe(rd, wr)\n\tif err != nil {\n\t\tcloseFunc() //nolint:errcheck\n\n\t\treturn nil, errors.Wrap(err, \"error creating sftp client pipe\")\n\t}\n\n\treturn &sftpConnection{\n\t\tcurrentClient: c,\n\t\tcloseFunc:     closeFunc,\n\t}, nil\n}\n\nfunc getSFTPClient(ctx context.Context, opt *Options) (*sftpConnection, error) {\n\tif opt.ExternalSSH {\n\t\treturn getSFTPClientExternal(ctx, opt)\n\t}\n\n\tconfig, err := createSSHConfig(ctx, opt)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/repo/blob/sftp/sftp_storage.go#L486-L522","documentation":"errors.Wrap around sftp.NewClientPipe(rd, wr) in getSFTPClientExternal. The SSH subprocess started, but the SFTP subsystem handshake over its stdin/stdout pipes failed — the remote side likely did not run an SFTP server, closed the pipe, or returned a protocol error. kopia closes the process (closeFunc) before returning this error to avoid leaking the spawned ssh process.","triggerScenarios":"sftp.NewClientPipe(rd, wr) fails because the remote sshd has no sftp subsystem enabled, the sftp server binary path in the ssh command is wrong, authentication failed, or the process died immediately after Start.","commonSituations":"Remote server's sshd_config lacks 'Subsystem sftp internal-sftp' (or points to a missing sftp-server binary); wrong username/password/key; server closed connection due to MaxStartups or fail2ban; custom SSH wrappers that print banners breaking the protocol stream.","solutions":["Verify the remote sshd has an sftp subsystem: check 'Subsystem sftp internal-sftp' (or sftp-server path) in /etc/ssh/sshd_config on the server.","Test manually with 'ssh user@host -s sftp' — if it fails or prints garbage, fix server-side SFTP config first.","Confirm credentials/key auth work for a plain ssh login; add -i <keyfile> or correct user to the sftp.command option.","Check the remote server logs (auth.log) for why the SFTP subsystem request was rejected (fail2ban, MaxStartups, disabled user)."],"exampleFix":"// before (server sshd_config)\n# Subsystem sftp internal-sftp   // commented out -> pipe handshake fails\n// after\nSubsystem sftp internal-sftp","handlingStrategy":"validation","validationCode":"out, err := exec.Command(\"ssh\", \"-o\", \"BatchMode=yes\", host, \"-s\", \"sftp\").CombinedOutput()\nif err != nil { return fmt.Errorf(\"sftp subsystem unavailable on %s: %v: %s\", host, err, out) }","typeGuard":null,"tryCatchPattern":"conn, err := storage.NewSFTP(ctx, opts)\nif err != nil && strings.Contains(err.Error(), \"error creating sftp client pipe\") {\n    // handshake failed: verify remote sftp subsystem and credentials\n    return fmt.Errorf(\"remote SFTP subsystem rejected the session: %w\", err)\n}","preventionTips":["Confirm 'Subsystem sftp internal-sftp' exists in the server's sshd_config.","Test 'ssh user@host -s sftp' manually before configuring kopia.","Use BatchMode key auth to avoid banner/prompt output corrupting the pipe protocol.","Keep fail2ban/MaxStartups from blocking repeated connections."],"tags":["ssh","sftp","pipe","protocol-handshake"],"backgroundTag":"broken-pipe","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}