Skip to main content

Exit Codes and Error Handling

Every rsync run returns an exit code that tells you exactly what happened — success, partial failure, network error, or something else. Building scripts that check and respond to these codes is what separates a fragile backup from a reliable one.

Exit Code Reference

CodeNameMeaningRecoverable?
0SuccessAll files transferred
1Syntax errorInvalid flags or argumentsFix command
2Protocol incompatibilityVersion mismatch between client/serverUpgrade rsync
3File selection errorSource path doesn't existFix paths
5Error starting client-serverDaemon/module issueCheck rsyncd config
10Socket I/O errorNetwork connection droppedRetry
11File I/O errorDisk read/write failureCheck disk
12Protocol data stream errorCorrupted transferRetry
13Diagnostics errorError with program diagnosticsCheck setup
14IPC errorInternal process communication failed
20Signal receivedrsync killed (e.g., SIGINT)Restart
21waitpid() errorProcess management error
22Memory allocation errorOut of RAMReduce scope
23Partial transfer (I/O)Some files failed (permissions, locked)Investigate
24Partial transfer (vanished)Files disappeared during scanUsually safe
25Max delete limit reached--max-delete was hitIntentional
30Send/receive timeoutTransfer stalledRetry with --timeout
35Daemon connection timeoutCouldn't reach rsync daemonRetry
255SSH errorSSH connection failedDebug SSH

Basic Error Handling

Check Exit Code

rsync -avz /src/ user@remote:/dest/
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
echo " Backup completed successfully"
elif [ $EXIT_CODE -eq 24 ]; then
echo " Some files vanished (usually harmless)"
else
echo " Backup failed with exit code $EXIT_CODE"
exit 1
fi

One-Liner for Cron

rsync -avz /src/ user@remote:/dest/ || echo "rsync failed ($?) at $(date)" >> /var/log/rsync-errors.log

Retry Logic

Simple Retry

for attempt in 1 2 3; do
rsync -avzP /src/ user@remote:/dest/ && break
echo "Attempt $attempt failed, retrying in 60s..."
sleep 60
done

Smart Retry (Only on Network Errors)

#!/bin/bash
# smart-retry-rsync.sh
MAX_RETRIES=3
RETRY_DELAY=60
NETWORK_ERRORS="10 12 30 35 255"

for attempt in $(seq 1 $MAX_RETRIES); do
rsync -avzP /src/ user@remote:/dest/
EXIT_CODE=$?

# Success
[ $EXIT_CODE -eq 0 ] && exit 0

# Vanished files (harmless)
[ $EXIT_CODE -eq 24 ] && exit 0

# Check if error is retryable
if echo "$NETWORK_ERRORS" | grep -qw "$EXIT_CODE"; then
echo "Network error (code $EXIT_CODE), retry $attempt/$MAX_RETRIES..."
sleep $RETRY_DELAY
else
echo "Fatal error (code $EXIT_CODE), not retrying."
exit $EXIT_CODE
fi
done

echo "All $MAX_RETRIES attempts failed"
exit 1

Alerting on Failure

Email Alert

#!/bin/bash
rsync -avz /src/ user@remote:/dest/ --log-file=/var/log/rsync/backup.log
EXIT_CODE=$?

if [ $EXIT_CODE -ne 0 ] && [ $EXIT_CODE -ne 24 ]; then
mail -s "Rsync backup failed (code $EXIT_CODE) on $(hostname)" \
admin@example.com < /var/log/rsync/backup.log
fi

Slack Webhook Alert

#!/bin/bash
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

rsync -avz /src/ user@remote:/dest/
EXIT_CODE=$?

if [ $EXIT_CODE -ne 0 ] && [ $EXIT_CODE -ne 24 ]; then
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\" Rsync failed on $(hostname) — exit code $EXIT_CODE\"}" \
"$WEBHOOK_URL"
fi

Production Backup Script

#!/bin/bash
# production-backup.sh — Full error handling, retry, and alerting
set -o pipefail

LOG="/var/log/rsync/backup_$(date +%F).log"
MAX_RETRIES=3

log() { echo "[$(date '+%H:%M:%S')] $1" | tee -a "$LOG"; }

run_backup() {
rsync -avz --stats \
--exclude='cache/' --exclude='*.log' --exclude='tmp/' \
--log-file="$LOG" \
/var/www/html/ user@backup:/backups/$(date +%F)/
return $?
}

# Retry loop
for attempt in $(seq 1 $MAX_RETRIES); do
log "Attempt $attempt/$MAX_RETRIES"
run_backup
EXIT_CODE=$?

case $EXIT_CODE in
0)
log " Backup completed successfully"
exit 0
;;
24)
log " Some files vanished (harmless)"
exit 0
;;
10|12|30|35|255)
log " Network error (code $EXIT_CODE), retrying..."
sleep 60
;;
*)
log " Fatal error (code $EXIT_CODE)"
mail -s "Backup FAILED on $(hostname)" admin@example.com < "$LOG"
exit $EXIT_CODE
;;
esac
done

log " All $MAX_RETRIES attempts failed"
mail -s "Backup FAILED after $MAX_RETRIES retries on $(hostname)" admin@example.com < "$LOG"
exit 1

Common Pitfalls

PitfallConsequencePrevention
Ignoring exit codes in cronSilent failures, no backups for daysAlways check $? and alert
Treating exit 24 as failureFalse alarms (temp files vanished)Handle 24 as warning, not error
Retrying non-recoverable errorsWasted time, same failureRetry only network errors (10, 12, 30)
No loggingCan't investigate after-the-factAlways use --log-file
set -e with rsyncExit 24 kills the scriptUse explicit exit code checking instead

Quick Reference

# Check exit code
rsync -av /src/ /dest/; echo "Exit: $?"

# Retry on failure
for i in 1 2 3; do rsync -avzP /src/ /dest/ && break || sleep 60; done

# Alert on failure
rsync -av /src/ /dest/ || echo "Failed ($?) at $(date)" >> /var/log/rsync-errors.log

# Handle vanished files gracefully
rsync -av /src/ /dest/; [ $? -eq 24 ] && echo "Vanished files, OK"

What's Next