SSH Subsystem Request Failed on Channel 0 With sftp
Fix SSH subsystem request failed on channel 0 with sftp or scp: a missing Subsystem line, wrong sftp-server path, shell startup output and chroots.

The message subsystem request failed on channel 0 followed by Connection closed or scp: Connection closed comes from SFTP and from scp in its SFTP mode, which has been the default since OpenSSH 9.0. The login worked, and the server declined to start the file transfer service. The causes are all server-side or in login scripts. The commands use OpenSSH 9.7; the client tools are covered in scp and sftp.
Check the Subsystem line
sudo sshd -T | grep -i subsystem
ls -l /usr/lib/openssh/sftp-server /usr/libexec/openssh/sftp-server 2>/dev/null
sudo grep -ri subsystem /etc/ssh/sshd_config /etc/ssh/sshd_config.d/subsystem sftp /usr/lib/openssh/sftp-server
A server without a Subsystem sftp line has no SFTP, and one with the wrong path fails when it tries to run the binary. Paths differ between distributions: /usr/lib/openssh/sftp-server on Debian and Ubuntu, /usr/libexec/openssh/sftp-server on RHEL and Fedora. The safest line is Subsystem sftp internal-sftp, which uses the code built into sshd and needs no external file. Reload with sudo sshd -t && sudo systemctl reload ssh after the change.
Login scripts that print text
ssh web1 true | od -c | head -3
grep -nE 'echo|printf|fortune|neofetch|motd' ~/.bashrc ~/.bash_profile ~/.profile ~/.zshenv 2>/dev/null0000000 W e l c o m e b a c k \n
SFTP is a binary protocol over the session's stdin and stdout. If the account's shell startup files print anything, a welcome message, a fortune, a conda or nvm banner, those bytes arrive before the protocol and the client reports Received message too long or subsystem request failed. Move such output inside an interactive check, [[ $- == *i* ]] && echo ..., or [[ -t 1 ]]. The check ssh host true must print nothing. This is the usual cause when only one account fails.
Restricted accounts and chroots
# /etc/ssh/sshd_config
Subsystem sftp internal-sftp
Match Group sftponly
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
A chrooted user cannot run an external sftp-server because the binary is outside the new root; internal-sftp solves that. The chroot directory must be owned by root and not writable by anyone else, or sshd refuses it with a "bad ownership or modes for chroot directory" log line. An account with /usr/sbin/nologin as its shell cannot start a subsystem unless ForceCommand internal-sftp is set for it.
Read the server log
sudo journalctl -u ssh -n 30 --no-pager
sudo tail -20 /var/log/auth.logThe log states the reason, for example "subsystem request for sftp by user alice failed, subsystem not found". See auditing SSH logins for more places to look.
Workarounds
scp -O file.txt alice@web1:/tmp/ # legacy scp protocol, no SFTP needed
ssh alice@web1 'cat > /tmp/file.txt' < file.txt
rsync -e ssh file.txt alice@web1:/tmp/The legacy scp protocol uses a remote scp command, and rsync needs only a shell. They are workarounds for hosts where you cannot change the server, but they inherit the same shell-output problem. See scp versus rsync versus sftp.
What happens in the handshake
After authentication, the client opens a session channel and requests a subsystem named sftp. The server looks up that name in its Subsystem directives, starts the configured program as the logged-in user, and connects it to the channel. If the name is missing, the program cannot start, or the user's login shell prevents the launch, the server answers with a failure, and the client prints "subsystem request failed on channel 0". The client has no further detail, which is why the server log is the place to look. The programs involved are small: sftp-server speaks the protocol on its standard input and output, and internal-sftp is the same code built into sshd so that it works inside a chroot with no external files.
Because the same channel carries the protocol, anything extra written to it before the server starts corrupts the exchange. That is the reason the shell startup problem is so common: the client expects a protocol version packet, receives the text of a welcome banner, and gives up. A test that exposes it is ssh host true | wc -c, which should print zero bytes.
Shell and account configuration
The account's login shell matters, because the server starts the subsystem through it for non-chroot setups. If the shell is /sbin/nologin, /bin/false or a restricted shell that refuses commands, the subsystem fails to start even though a plain login could be intended to work. Set a real shell, or use ForceCommand internal-sftp in a Match block, which bypasses the shell for those accounts. Also check AllowUsers, AllowGroups and DenyUsers, which can accept a login but be combined with a Match block that sets a different command for some groups.
Disk and resource problems appear here too. A full filesystem for the home directory, an exhausted process limit for the user, or a quota can stop the server process from starting and show up as the same message. The logs list the real error, so read them before changing configuration.
Managed and appliance targets
Routers, storage appliances and some managed hosting platforms run an SSH server with no SFTP or only a limited command set. The failure is then a property of the target and not a fault to fix. The options on the client side are the legacy mode with scp -O, which asks the remote side to run an scp command if it exists, or a pipe through the shell. Check the vendor documentation for the supported transfer method, which is often a web upload, an API or a different protocol on another port, and use that instead of fighting the SSH server.
Confirming the fix
After a change, restart the daemon with a configuration test first, sudo sshd -t, which reports syntax errors before they lock anyone out, and keep an existing session open while testing with a new one. Then run sftp -v user@host and look for the line showing the subsystem request succeeding, followed by the version exchange. Run a real transfer of a small file in both directions, and from the account that will use the service, not from an administrator. Document the cause in the change record, because this error returns after OS upgrades that replace configuration files, and a known cause saves an hour of searching.
Checking from the client side
The client can give a few useful hints before the server logs are consulted. Run sftp -vvv user@host and compare the sequence with a working host: the point where the output stops is the failed step. If the server prints a banner before the protocol, the debug output shows unexpected text received. Another quick test is ssh user@host 'ls /usr/lib/openssh', which confirms the path exists for that account. If the account has a restricted shell, that command also fails, which points to the shell and not the SFTP configuration. Write down what each check showed, because the combination of shell, banner and path usually identifies the cause within a few minutes.
Where this does not apply
Managed hosts that disable SFTP on purpose, such as some Git services, will not accept it however you configure the client. See sshd_config(5) Subsystem and sftp-server(8).


