{"record":{"id":"3d72a05e49369c5c","repo":"restic/restic","slug":"sftp-no-space-left-on-device","errorCode":null,"errorMessage":"sftp: no space left on device","messagePattern":"sftp: no space left on device","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/sftp/sftp.go","lineNumber":436,"sourceCode":"// on the remote, and if so, makes it permanent.\nfunc (r *SFTP) checkNoSpace(dir string, size int64, origErr error) error {\n\t// The SFTP protocol has a message for ENOSPC,\n\t// but pkg/sftp doesn't export it and OpenSSH's sftp-server\n\t// sends FX_FAILURE instead.\n\n\te, ok := origErr.(*sftp.StatusError)\n\t_, hasExt := r.c.HasExtension(\"statvfs@openssh.com\")\n\tif !ok || e.FxCode() != sftp.ErrSSHFxFailure || !hasExt {\n\t\treturn origErr\n\t}\n\n\tfsinfo, err := r.c.StatVFS(dir)\n\tif err != nil {\n\t\tdebug.Log(\"sftp: StatVFS returned %v\", err)\n\t\treturn origErr\n\t}\n\tif fsinfo.Favail == 0 || fsinfo.Frsize*fsinfo.Bavail < uint64(size) {\n\t\terr := errors.New(\"sftp: no space left on device\")\n\t\treturn backoff.Permanent(err)\n\t}\n\treturn origErr\n}\n\n// Load runs fn with a reader that yields the contents of the file at h at the\n// given offset.\nfunc (r *SFTP) Load(ctx context.Context, h backend.Handle, length int, offset int64, fn func(rd io.Reader) error) error {\n\tif err := r.clientError(); err != nil {\n\t\treturn err\n\t}\n\n\treturn util.DefaultLoad(ctx, h, length, offset, r.openReader, func(rd io.Reader) error {\n\t\tif length == 0 || !feature.Flag.Enabled(feature.BackendErrorRedesign) {\n\t\t\treturn fn(rd)\n\t\t}\n\n\t\t// there is no direct way to efficiently check whether the file is too short","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/restic/restic/blob/a80be1478a4c537f8396e0db2b05120aa78f11e0/internal/backend/sftp/sftp.go#L418-L454","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n$ restic -r sftp:admin@nas:/srv/restic backup ./bigdir\n# sftp: no space left on device\n\n# after (on the server)\n$ df -h /srv/restic && df -i /srv/restic\n$ rm -rf /srv/old-exports   # or grow the volume\n$ restic -r sftp:admin@nas:/srv/restic backup ./bigdir","handlingStrategy":"validation","validationCode":"// estimate need before large writes: statvfs the repo dir via ssh\nout, err := exec.Command(\"ssh\", host, \"df -B1 --output=avail \"+repoDir).Output()\nif err == nil {\n\tavail, _ := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)\n\tif avail < expectedPackBytes {\n\t\treturn errors.New(\"insufficient space on remote repo volume\")\n\t}\n}","typeGuard":null,"tryCatchPattern":"err := be.Save(ctx, h, rd)\nif err != nil {\n\tif strings.Contains(err.Error(), \"no space left on device\") {\n\t\t// permanent: stop the run, alert, do not retry\n\t\treturn fmt.Errorf(\"remote volume full; free space or prune before retrying: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["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."],"tags":["sftp","disk-full","capacity","permanent-error","storage"],"backgroundTag":null,"analyzedSha":"a80be1478a4c537f8396e0db2b05120aa78f11e0","analyzedAt":"2026-08-15T15:30:29.928Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}