Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Automation By James Joyner IV · · 8 min read Last reviewed Jul 2026

rsync Error: 'connection unexpectedly closed (code 12)' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix rsync: connection unexpectedly closed (0 bytes received so far) (code 12) — remote rsync missing, contaminated shell output, and SSH transport failures.

  • #automation
  • #troubleshooting
  • #rsync
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

Exit code 12 is a protocol error: the local rsync expected to speak to a remote rsync over the SSH channel, but the channel closed or returned data that was not a valid rsync protocol stream. The tell-tale (0 bytes received so far) means the handshake never even started — the remote side died, was never there, or the transport carried garbage instead of protocol.

$ rsync -a /opt/jobs/src/ myuser@example.com:/opt/jobs/dst/
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: error in rsync protocol data stream (code 12) at io.c(232) [Receiver=3.2.7]

This is almost always a transport or remote-environment problem, not a file problem. The two dominant causes are (1) the remote host has no rsync binary to launch, and (2) the remote login shell prints text — a banner, an MOTD, a stray echo in .bashrc — that contaminates the stream rsync tries to parse. 0 bytes received is the fingerprint of a broken channel; a non-zero byte count points instead to a version or data-stream mismatch.

Symptoms

  • rsync exits with status 12 and connection unexpectedly closed (0 bytes received so far).
  • Plain ssh myuser@example.com works fine, but rsync over that same SSH fails instantly.
  • The failure is immediate — no files transfer, no progress.
  • The remote host was recently rebuilt, reimaged, or is a minimal container.
  • The remote user’s shell prints a login banner or a .bashrc echoes text on non-interactive logins.
echo "rsync exit: $?"
rsync exit: 12

Common Root Causes

1. rsync is not installed on the remote host

rsync launches rsync --server on the far end over SSH. If the binary is missing, the remote command fails and the channel closes with zero protocol bytes.

bash: rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [sender]

2. The remote login shell prints text into the stream

An MOTD, a Last login: banner, or an echo/fortune in ~/.bashrc/~/.bash_profile that runs on non-interactive logins injects bytes rsync then fails to parse as protocol.

Welcome to prod-app-02 — authorized use only
rsync error: error in rsync protocol data stream (code 12)

3. SSH authentication or connection failure

If SSH itself cannot authenticate (expired key, wrong user, host-key change), the command never runs and rsync sees a closed channel.

4. Wrong remote path or permissions

A destination path the remote user cannot access can cause the remote rsync to exit early before establishing the stream.

5. Protocol / version mismatch

A very old rsync on one side talking to a much newer one can fail to negotiate, though this usually shows a non-zero received-bytes count.

6. Remote rsync killed (OOM) mid-handshake

On a memory-starved host, the kernel OOM-killer terminates rsync --server as it starts, closing the stream.

How to Diagnose

Step 1: Confirm SSH itself works, then run rsync’s SSH verbosely

ssh myuser@example.com 'echo ok'
rsync -e 'ssh -v' -a /opt/jobs/src/ myuser@example.com:/opt/jobs/dst/ 2>&1 | tail -20

If ssh ... echo ok works but rsync fails, the problem is on the remote rsync side or the shell environment, not connectivity.

Step 2: Check that rsync exists on the remote

ssh myuser@example.com 'command -v rsync || echo MISSING'
MISSING

MISSING is the direct cause — install it (see Fixes).

Step 3: Detect a contaminated non-interactive shell

This is the single most common subtle cause. A clean non-interactive login must print nothing:

ssh myuser@example.com 'true' 2>/dev/null | xxd | head
00000000: 5765 6c63 6f6d 6520 746f 2070 726f 6420  Welcome to prod

Any output at all here means the shell is injecting bytes into rsync’s channel.

Step 4: Find the offending echo

ssh myuser@example.com 'grep -nE "echo|fortune|cat .*motd" ~/.bashrc ~/.bash_profile 2>/dev/null'
/home/myuser/.bashrc:14:echo "Welcome to $(hostname)"

Step 5: Compare rsync versions on both ends

rsync --version | head -1
ssh myuser@example.com 'rsync --version | head -1'
rsync  version 3.2.7  protocol version 31
rsync  version 3.1.2  protocol version 31

Fixes

Install rsync on the remote host:

ssh myuser@example.com 'sudo apt-get update && sudo apt-get install -y rsync'
# or: sudo yum install -y rsync

Stop the login shell from printing on non-interactive sessions. Guard any output in .bashrc so it only runs for interactive shells:

# Near the top of ~/.bashrc
case $- in
  *i*) ;;      # interactive — banners OK
  *) return;;  # non-interactive (rsync/scp) — print nothing
esac

Suppress the MOTD/last-login banner for automation by touching a hushlogin or using a quiet SSH invocation:

ssh myuser@example.com 'touch ~/.hushlogin'

Point rsync at an explicit remote binary path if it is installed somewhere non-standard:

rsync -a --rsync-path=/usr/local/bin/rsync /opt/jobs/src/ myuser@example.com:/opt/jobs/dst/

After the fix, a clean run transfers files and exits 0:

rsync -a /opt/jobs/src/ myuser@example.com:/opt/jobs/dst/ && echo "exit: $?"
exit: 0

What to Watch Out For

  • 0 bytes received so far almost always means the channel broke before protocol negotiation — look at the remote environment, not your files.
  • .bashrc output that seems harmless in an interactive terminal silently breaks rsync (and scp/sftp) on non-interactive logins.
  • Test non-interactive cleanliness with ssh host 'true' | xxd — any bytes are a problem.
  • Code 12 is a protocol error, distinct from code 23 (partial transfer); do not apply attribute fixes here.
  • Minimal container images and freshly reimaged hosts frequently ship without rsync — bake it into the image.
Free download · 368-page PDF

Fixed it? Get 500 Automation & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.