Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Troubleshooting

scp: Connection Closed — Why SSH Works but the Copy Does Not

Sean

Platform Writer

Aug 26, 2026
8 min read

If ssh user@host works and scp file user@host: returns Connection closed instantly, the transport is fine. The usual cause is that something in the login sequence writes to stdout and corrupts the binary protocol scp expects — or, on newer clients, that scp now uses SFTP and the server has no SFTP subsystem enabled.

scp: Connection Closed — Why SSH Works but the Copy Does Not

This one is genuinely confusing the first time, because every diagnostic you reach for says the connection is healthy. You can ssh in. You can run commands. Port 22 is open. And scp gives up before it has transferred a byte.

The reason is that scp is not just ssh with a file on the end. It opens a channel and then speaks a protocol over it, and that protocol assumes it has the channel to itself.

Table of contents

The login banner problem

This is the cause in most cases where interactive ssh works and scp does not.

When scp connects, it expects the very first bytes on the channel to be protocol data. If your .bashrc, .bash_profile, /etc/profile or a system MOTD script writes anything — a welcome message, a fortune, a disk usage summary, a neofetch — that text arrives first, scp reads it as a malformed protocol message, and closes.

Interactive ssh does not care, because a human reading a banner is the intended behaviour.

Confirm it in one command. This runs a non-interactive command, exactly as scp would, and shows anything that leaks out:

ssh user@host 'true'
# any output at all here is your problem

Clean output means the banner is not your issue. Any text means it is.

The fix is to guard the noisy part of your shell config so it only runs for interactive sessions:

# at the top of ~/.bashrc on the SERVER
case $- in
  *i*) ;;
    *) return;;
esac

That returns early for non-interactive shells. Everything below it — aliases, prompts, welcome messages — stops running for scp, rsync, and anything else that expects a clean channel. This is standard in the default Debian and Ubuntu .bashrc, which is why it is usually a hand-edited config that breaks it.

The SFTP subsystem problem on newer clients

OpenSSH 9.0 changed scp’s default backend from the old SCP protocol to SFTP. That was a security improvement, and it broke transfers to servers that never enabled the SFTP subsystem.

The error can look like this:

subsystem request failed on channel 0
scp: Connection closed

That wording is close to definitive — it is the subsystem, not the shell.

Check the server’s config:

grep -i subsystem /etc/ssh/sshd_config
# want: Subsystem sftp /usr/lib/openssh/sftp-server

If the line is missing or commented, add it and reload sshd. If you cannot change the server — a network appliance, a vendor-managed box, an embedded device — force the old protocol from the client instead:

scp -O file.txt user@host:/tmp/

The -O flag says use the legacy SCP protocol. It is the right escape hatch for devices whose firmware you do not control, and the wrong long-term answer for a server you do.

Reading the actual error instead of guessing

Three levels of -v will tell you where in the sequence it dies, which separates these causes immediately:

scp -vvv file.txt user@host:/tmp/ 2>&1 | tail -40

What to look for in the tail:

  • Authentication succeeded, then Sending command: scp -t /tmp/, then close — a shell-output or permissions problem on the far side.
  • subsystem request failed — the SFTP subsystem is missing, see above.
  • Failure before Authenticated to — this is not really an scp problem, it is an auth problem that ssh would hit too.
  • Received disconnect ... Too many authentication failures — your agent is offering keys until the server cuts you off; add -o IdentitiesOnly=yes -i /path/to/key.

The server’s log is the other half of the picture. On the remote host, journalctl -u ssh -n 50 or tail /var/log/auth.log frequently states the reason plainly when the client output is vague.

Destination problems that present as a closed connection

scp is not always articulate about the far side refusing the write. A few cases produce a terse close rather than a clear message:

  • The destination directory does not exist. scp will not create parent directories. scp file user@host:/opt/app/releases/ fails if releases is missing.
  • No write permission. The path exists and the user cannot write to it. Check with ssh user@host 'test -w /opt/app && echo ok'.
  • The disk is full. ssh user@host 'df -h /opt' — a full filesystem produces some genuinely unhelpful client-side errors.
  • A restricted shell. If the account’s shell is rbash, /sbin/nologin, or a forced command in authorized_keys, scp cannot invoke its remote helper at all.

That last one is worth checking explicitly on hardened or CI-specific accounts, because it is deliberate configuration rather than a mistake:

ssh user@host 'echo $SHELL; grep "^user:" /etc/passwd'

When to stop debugging scp and use something else

scp is fine for one file. For anything you do more than occasionally, rsync is better and it fails more informatively:

rsync -avz --progress file.txt user@host:/tmp/

It resumes interrupted transfers, skips unchanged files, preserves permissions properly, and its error messages name the actual problem more often than scp’s do.

sftp is the other option, and it is useful specifically as a diagnostic — if sftp user@host connects and lets you put a file while scp fails, you have narrowed the fault to scp’s protocol handling rather than the connection.

The larger point: if copying files onto a server by hand is part of how software gets deployed, the transfer mechanism is not really the problem. A deploy that depends on someone remembering the right path is a deploy that will eventually go to the wrong path.

How this fits the rest of the stack

Most Connection closed hunts end at a shell config someone edited eighteen months ago, which is a good argument for not putting deployment on the far side of a hand-run copy command at all. Build artefacts that arrive by scp have no record of who sent them, what was in them, or how to get the previous version back.

Deploying from a repository fixes that by making the artefact reproducible: the commit is the record, the build log shows what happened, and rolling back is selecting a previous deploy rather than finding an old tarball. RunxBuild builds services and static sites from a connected GitHub repository with per-deploy logs and rollback, so there is no manual copy step to debug. If you want to compare what that costs against a box you administer yourself, the RunxBuild hosting calculator itemises the service, the database, the storage and the bandwidth separately.

Useful related references:

FAQ

Why does ssh work but scp fail with connection closed?

Interactive ssh tolerates output from login scripts because a human is meant to read it. scp expects the channel to carry only protocol data, so any banner, MOTD, or echo in .bashrc corrupts the first message it reads. Run ssh user@host 'true' — if that prints anything at all, that output is the cause.

What does subsystem request failed on channel 0 mean?

OpenSSH 9.0 and later use SFTP as scp’s backend, and the server has no SFTP subsystem configured. Add Subsystem sftp /usr/lib/openssh/sftp-server to /etc/ssh/sshd_config and reload sshd, or pass scp -O to force the legacy protocol if you cannot change the server.

How do I stop my .bashrc from breaking scp?

Guard it so it returns early for non-interactive shells. Put case $- in *i*) ;; *) return;; esac at the top of ~/.bashrc on the server. Everything after it stops running for scp, rsync and other non-interactive sessions.

Is rsync better than scp?

For anything repeated, yes. It resumes partial transfers, skips unchanged files, preserves permissions correctly and reports errors more clearly. scp remains fine for a single ad-hoc file, and OpenSSH’s own maintainers have described the scp protocol as outdated.

Can a full disk cause scp connection closed?

Yes, and it is a common false lead because the client message rarely mentions disk space. Check with ssh user@host 'df -h /path' before spending time on ssh configuration. A missing destination directory produces similarly vague output, since scp will not create parent directories.

#scp connection closed#scp#ssh#sftp#linux