restic/restic · error
sftp: no space left on device
Error message
sftp: no space left on device
What it means
isReachable-style handling in the SFTP backend: when a write fails with an SSH_FX_FAILURE status and the server supports statvfs@openssh.com, restic stats the target directory. If free file slots are zero (Favail == 0) or free bytes (Frsize*Bavail) are below the requested size, it converts the failure into a permanent 'no space left on device' error so retry loops stop immediately instead of hammering a full disk.
Source
Thrown at internal/backend/sftp/sftp.go:436
// on the remote, and if so, makes it permanent.
func (r *SFTP) checkNoSpace(dir string, size int64, origErr error) error {
// The SFTP protocol has a message for ENOSPC,
// but pkg/sftp doesn't export it and OpenSSH's sftp-server
// sends FX_FAILURE instead.
e, ok := origErr.(*sftp.StatusError)
_, hasExt := r.c.HasExtension("statvfs@openssh.com")
if !ok || e.FxCode() != sftp.ErrSSHFxFailure || !hasExt {
return origErr
}
fsinfo, err := r.c.StatVFS(dir)
if err != nil {
debug.Log("sftp: StatVFS returned %v", err)
return origErr
}
if fsinfo.Favail == 0 || fsinfo.Frsize*fsinfo.Bavail < uint64(size) {
err := errors.New("sftp: no space left on device")
return backoff.Permanent(err)
}
return origErr
}
// Load runs fn with a reader that yields the contents of the file at h at the
// given offset.
func (r *SFTP) Load(ctx context.Context, h backend.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
if err := r.clientError(); err != nil {
return err
}
return util.DefaultLoad(ctx, h, length, offset, r.openReader, func(rd io.Reader) error {
if length == 0 || !feature.Flag.Enabled(feature.BackendErrorRedesign) {
return fn(rd)
}
// there is no direct way to efficiently check whether the file is too shortView on GitHub (pinned to a80be1478a)
Solutions
- Free space on the remote filesystem (delete/prune data, raise the quota) and retry.
- If inodes are exhausted (Favail == 0), remove many small files or reformat with more inodes.
- Run 'restic prune' against the repo to shrink it once any space is recoverable.
- Point the repository at a larger volume if growth is expected.
Example fix
# before $ restic -r sftp:admin@nas:/srv/restic backup ./bigdir # sftp: no space left on device # after (on the server) $ df -h /srv/restic && df -i /srv/restic $ rm -rf /srv/old-exports # or grow the volume $ restic -r sftp:admin@nas:/srv/restic backup ./bigdir
Defensive patterns
Strategy: validation
Validate before calling
// estimate need before large writes: statvfs the repo dir via ssh
out, err := exec.Command("ssh", host, "df -B1 --output=avail "+repoDir).Output()
if err == nil {
avail, _ := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)
if avail < expectedPackBytes {
return errors.New("insufficient space on remote repo volume")
}
} Try / catch
err := be.Save(ctx, h, rd)
if err != nil {
if strings.Contains(err.Error(), "no space left on device") {
// permanent: stop the run, alert, do not retry
return fmt.Errorf("remote volume full; free space or prune before retrying: %w", err)
}
return err
} Prevention
- Monitor free space and inodes on the volume hosting sftp repos.
- Prune regularly and alert below a size threshold larger than your biggest backup.
- Treat this error as terminal for the run; retries cannot succeed without freeing space.
When it happens
Trigger: Saving a pack file whose size exceeds free bytes on the remote filesystem; inode exhaustion on the server (Favail == 0) even with bytes free; requires the server to support statvfs@openssh.com or the original error passes through unchanged.
Common situations: Full or quota-capped remote volumes; small VPS/home-NAS partitions hosting the repo; inode exhaustion from millions of small files; size mispredictions during large backup uploads.
Related errors
- rclone stdio connection already closed
- sftp: invalid format, hostname or path not found
- invalid format, does not start with "sftp:"
- sftp path starts with the tilde (~) character, that fails fo
- cannot specify both sftp.command and sftp.args options
AI-assisted analysis of restic/restic@a80be1478a (2026-08-15).
Data as JSON: /api/errors/3d72a05e49369c5c.
Report an issue: GitHub.