Repository: restic/restic
Pull request #5122 (2024-11-01 15:37:33)

Bump go and golangci lint version
<!--
Thank you very much for contributing code or documentation to restic! Please
fill out the following questions to make it easier for us to review your
changes.
-->

What does this PR change? What problem does it solve?
-----------------------------------------------------
Bump Go and golangci-lint used by our CI tests to the latest version.
<!--
Describe the changes and their purpose here, as detailed as needed.
-->

Was the change previously discussed in an issue or on the forum?
----------------------------------------------------------------
No.
<!--
Link issues and relevant forum posts here.

If this PR resolves an issue on GitHub, use "Closes #1234" so that the issue
is closed automatically when this PR is merged.
-->

Checklist
---------

<!--
You do not need to check all the boxes below all at once. Feel free to take
your time and add more commits. If you're done and ready for review, please
check the last box. Enable a checkbox by replacing [ ] with [x].
-->

- [x] I have read the [contribution guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches).
- [x] I have [enabled maintainer edits](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
- [x] I have added tests for all code changes.
- ~~[ ] I have added documentation for relevant changes (in the manual).~~
- ~~[ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (see [template](https://github.com/restic/restic/blob/master/changelog/TEMPLATE)).~~
- [x] I have run `gofmt` on the code in all commits.
- [x] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits).
- [x] I'm done! This pull request is ready for review.



Patch:
diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go
index 2213d8e7a..06ae6cc20 100644
--- a/cmd/restic/cmd_ls.go
+++ b/cmd/restic/cmd_ls.go
@@ -75,17 +75,17 @@ func init() {
 }
 
 type lsPrinter interface {
-	Snapshot(sn *restic.Snapshot)
-	Node(path string, node *restic.Node, isPrefixDirectory bool)
-	LeaveDir(path string)
-	Close()
+	Snapshot(sn *restic.Snapshot) error
+	Node(path string, node *restic.Node, isPrefixDirectory bool) error
+	LeaveDir(path string) error
+	Close() error
 }
 
 type jsonLsPrinter struct {
 	enc *json.Encoder
 }
 
-func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) {
+func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) error {
 	type lsSnapshot struct {
 		*restic.Snapshot
 		ID          *restic.ID `json:"id"`
@@ -94,27 +94,21 @@ func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) {
 		StructType  string     `json:"struct_type"`  // "snapshot", deprecated
 	}
 
-	err := p.enc.Encode(lsSnapshot{
+	return p.enc.Encode(lsSnapshot{
 		Snapshot:    sn,
 		ID:          sn.ID(),
 		ShortID:     sn.ID().Str(),
 		MessageType: "snapshot",
 		StructType:  "snapshot",
 	})
-	if err != nil {
-		Warnf("JSON encode failed: %v\n", err)
-	}
 }
 
 // Print node in our custom JSON format, followed by a newline.
-func (p *jsonLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) {
+func (p *jsonLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) error {
 	if isPrefixDirectory {
-		return
-	}
-	err := lsNodeJSON(p.enc, path, node)
-	if err != nil {
-		Warnf("JSON encode failed: %v\n", err)
+		return nil
 	}
+	return lsNodeJSON(p.enc, path, node)
 }
 
 func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
@@ -160,8 +154,8 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
 	return enc.Encode(n)
 }
 
-func (p *jsonLsPrinter) LeaveDir(_ string) {}
-func (p *jsonLsPrinter) Close()            {}
+func (p *jsonLsPrinter) LeaveDir(_ string) error { return nil }
+func (p *jsonLsPrinter) Close() error            { return nil }
 
 type ncduLsPrinter struct {
 	out   io.Writer
@@ -171,16 +165,17 @@ type ncduLsPrinter struct {
 // lsSnapshotNcdu prints a restic snapshot in Ncdu save format.
 // It opens the JSON list. Nodes are added with lsNodeNcdu and the list is closed by lsCloseNcdu.
 // Format documentation: https://dev.yorhel.nl/ncdu/jsonfmt
-func (p *ncduLsPrinter) Snapshot(sn *restic.Snapshot) {
+func (p *ncduLsPrinter) Snapshot(sn *restic.Snapshot) error {
 	const NcduMajorVer = 1
 	const NcduMinorVer = 2
 
 	snapshotBytes, err := json.Marshal(sn)
 	if err != nil {
-		Warnf("JSON encode failed: %v\n", err)
+		return err
 	}
 	p.depth++
-	fmt.Fprintf(p.out, "[%d, %d, %s, [{\"name\":\"/\"}", NcduMajorVer, NcduMinorVer, string(snapshotBytes))
+	_, err = fmt.Fprintf(p.out, "[%d, %d, %s, [{\"name\":\"/\"}", NcduMajorVer, NcduMinorVer, string(snapshotBytes))
+	return err
 }
 
 func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
@@ -232,27 +227,30 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
 	return json.Marshal(outNode)
 }
 
-func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) {
+func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) error {
 	out, err := lsNcduNode(path, node)
 	if err != nil {
-		Warnf("JSON encode failed: %v\n", err)
+		return err
 	}
 
 	if node.Type == restic.NodeTypeDir {
-		fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat("  ", p.depth), strings.Repeat("  ", p.depth+1), string(out))
+		_, err = fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat("  ", p.depth), strings.Repeat("  ", p.depth+1), string(out))
 		p.depth++
 	} else {
-		fmt.Fprintf(p.out, ",\n%s%s", strings.Repeat("  ", p.depth), string(out))
+		_, err = fmt.Fprintf(p.out, ",\n%s%s", strings.Repeat("  ", p.depth), string(out))
 	}
+	return err
 }
 
-func (p *ncduLsPrinter) LeaveDir(_ string) {
+func (p *ncduLsPrinter) LeaveDir(_ string) error {
 	p.depth--
-	fmt.Fprintf(p.out, "\n%s]", strings.Repeat("  ", p.depth))
+	_, err := fmt.Fprintf(p.out, "\n%s]", strings.Repeat("  ", p.depth))
+	return err
 }
 
-func (p *ncduLsPrinter) Close() {
-	fmt.Fprint(p.out, "\n]\n]\n")
+func (p *ncduLsPrinter) Close() error {
+	_, err := fmt.Fprint(p.out, "\n]\n]\n")
+	return err
 }
 
 type textLsPrinter struct {
@@ -261,17 +259,23 @@ type textLsPrinter struct {
 	HumanReadable bool
 }
 
-func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) {
+func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) error {
 	Verbosef("%v filtered by %v:\n", sn, p.dirs)
+	return nil
 }
-func (p *textLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) {
+func (p *textLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) error {
 	if !isPrefixDirectory {
 		Printf("%s\n", formatNode(path, node, p.ListLong, p.HumanReadable))
 	}
+	return nil
 }
 
-func (p *textLsPrinter) LeaveDir(_ string) {}
-func (p *textLsPrinter) Close()            {}
+func (p *textLsPrinter) LeaveDir(_ string) error {
+	return nil
+}
+func (p *textLsPrinter) Close() error {
+	return nil
+}
 
 func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string) error {
 	if len(args) == 0 {
@@ -374,7 +378,9 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 		return err
 	}
 
-	printer.Snapshot(sn)
+	if err := printer.Snapshot(sn); err != nil {
+		return err
+	}
 
 	processNode := func(_ restic.ID, nodepath string, node *restic.Node, err error) error {
 		if err != nil {
@@ -387,7 +393,9 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 		printedDir := false
 		if withinDir(nodepath) {
 			// if we're within a target path, print the node
-			printer.Node(nodepath, node, false)
+			if err := printer.Node(nodepath, node, false); err != nil {
+				return err
+			}
 			printedDir = true
 
 			// if recursive listing is requested, signal the walker that it
@@ -402,7 +410,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 		if approachingMatchingTree(nodepath) {
 			// print node leading up to the target paths
 			if !printedDir {
-				printer.Node(nodepath, node, true)
+				return printer.Node(nodepath, node, true)
 			}
 			return nil
 		}
@@ -412,7 +420,9 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 		if node.Type == restic.NodeTypeDir {
 			// immediately generate leaveDir if the directory is skipped
 			if printedDir {
-				printer.LeaveDir(nodepath)
+				if err := printer.LeaveDir(nodepath); err != nil {
+					return err
+				}
 			}
 			return walker.ErrSkipNode
 		}
@@ -421,11 +431,12 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 
 	err = walker.Walk(ctx, repo, *sn.Tree, walker.WalkVisitor{
 		ProcessNode: processNode,
-		LeaveDir: func(path string) {
+		LeaveDir: func(path string) error {
 			// the root path `/` has no corresponding node and is thus also skipped by processNode
 			if path != "/" {
-				printer.LeaveDir(path)
+				return printer.LeaveDir(path)
 			}
+			return nil
 		},
 	})
 
@@ -433,6 +444,5 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
 		return err
 	}
 
-	printer.Close()
-	return nil
+	return printer.Close()
 }
diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go
index a9f664110..b62d1ed95 100644
--- a/cmd/restic/cmd_rewrite.go
+++ b/cmd/restic/cmd_rewrite.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"context"
-	"fmt"
 	"time"
 
 	"github.com/spf13/cobra"
@@ -141,7 +140,7 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti
 			if selectByName(path) {
 				return node
 			}
-			Verbosef(fmt.Sprintf("excluding %s\n", path))
+			Verbosef("excluding %s\n", path)
 			return nil
 		}
 
diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go
index 466f536e0..f935cec86 100644
--- a/cmd/restic/cmd_snapshots.go
+++ b/cmd/restic/cmd_snapshots.go
@@ -296,7 +296,9 @@ func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error {
 	}
 
 	// Info
-	fmt.Fprintf(stdout, "snapshots")
+	if _, err := fmt.Fprintf(stdout, "snapshots"); err != nil {
+		return err
+	}
 	var infoStrings []string
 	if key.Hostname != "" {
 		infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
@@ -308,11 +310,13 @@ func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error {
 		infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
 	}
 	if infoStrings != nil {
-		fmt.Fprintf(stdout, " for (%s)", strings.Join(infoStrings, ", "))
+		if _, err := fmt.Fprintf(stdout, " for (%s)", strings.Join(infoStrings, ", ")); err != nil {
+			return err
+		}
 	}
-	fmt.Fprintf(stdout, ":\n")
+	_, err = fmt.Fprintf(stdout, ":\n")
 
-	return nil
+	return err
 }
 
 // Snapshot helps to print Snapshots as JSON with their ID included.
diff --git a/cmd/restic/global.go b/cmd/restic/global.go
index fc52882d7..b824b7449 100644
--- a/cmd/restic/global.go
+++ b/cmd/restic/global.go
@@ -308,7 +308,7 @@ func readPasswordTerminal(ctx context.Context, in *os.File, out *os.File, prompt
 	fd := int(out.Fd())
 	state, err := term.GetState(fd)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "unable to get terminal state: %v\n", err)
+		_, _ = fmt.Fprintf(os.Stderr, "unable to get terminal state: %v\n", err)
 		return "", err
 	}
 
@@ -317,16 +317,22 @@ func readPasswordTerminal(ctx context.Context, in *os.File, out *os.File, prompt
 
 	go func() {
 		defer close(done)
-		fmt.Fprint(out, prompt)
+		_, err = fmt.Fprint(out, prompt)
+		if err != nil {
+			return
+		}
 		buf, err = term.ReadPassword(int(in.Fd()))
-		fmt.Fprintln(out)
+		if err != nil {
+			return
+		}
+		_, err = fmt.Fprintln(out)
 	}()
 
 	select {
 	case <-ctx.Done():
 		err := term.Restore(fd, state)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "unable to restore terminal state: %v\n", err)
+			_, _ = fmt.Fprintf(os.Stderr, "unable to restore terminal state: %v\n", err)
 		}
 		return "", ctx.Err()
 	case <-done:
diff --git a/cmd/restic/main.go b/cmd/restic/main.go
index 4cb135c48..096c5695c 100644
--- a/cmd/restic/main.go
+++ b/cmd/restic/main.go
@@ -140,7 +140,7 @@ func printExitError(code int, message string) {
 			return
 		}
 	} else {
-		fmt.Fprintf(globalOptions.stderr, "%v\n", message)
+		_, _ = fmt.Fprintf(globalOptions.stderr, "%v\n", message)
 	}
 }
 
@@ -152,10 +152,10 @@ func main() {
 	log.SetOutput(logBuffer)
 
 	err := feature.Flag.Apply(os.Getenv("RESTIC_FEATURES"), func(s string) {
-		fmt.Fprintln(os.Stderr, s)
+		_, _ = fmt.Fprintln(os.Stderr, s)
 	})
 	if err != nil {
-		fmt.Fprintln(os.Stderr, err)
+		_, _ = fmt.Fprintln(os.Stderr, err)
 		Exit(1)
 	}
 
diff --git a/internal/debug/debug.go b/internal/debug/debug.go
index 7bc3291d1..a09d6e74a 100644
--- a/internal/debug/debug.go
+++ b/internal/debug/debug.go
@@ -120,7 +120,7 @@ func goroutineNum() int {
 	runtime.Stack(b, false)
 	var num int
 
-	fmt.Sscanf(string(b), "goroutine %d ", &num)
+	_, _ = fmt.Sscanf(string(b), "goroutine %d ", &num)
 	return num
 }
 
diff --git a/internal/debug/round_tripper.go b/internal/debug/round_tripper.go
index 9dced95c6..4afab7298 100644
--- a/internal/debug/round_tripper.go
+++ b/internal/debug/round_tripper.go
@@ -42,7 +42,7 @@ func (rd *eofDetectReader) Close() error {
 			msg += fmt.Sprintf(", body: %q", buf)
 		}
 
-		fmt.Fprintln(os.Stderr, msg)
+		_, _ = fmt.Fprintln(os.Stderr, msg)
 		Log("%s: %+v", msg, errors.New("Close()"))
 	}
 	return rd.rd.Close()
diff --git a/internal/ui/termstatus/status.go b/internal/ui/termstatus/status.go
index 39654cc8c..e65330958 100644
--- a/internal/ui/termstatus/status.go
+++ b/internal/ui/termstatus/status.go
@@ -212,7 +212,7 @@ func (t *Terminal) runWithoutStatus(ctx context.Context) {
 			}
 
 			if _, err := io.WriteString(dst, msg.line); err != nil {
-				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
+				_, _ = fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
 			}
 
 			if flush == nil {
@@ -220,16 +220,18 @@ func (t *Terminal) runWithoutStatus(ctx context.Context) {
 			}
 
 			if err := flush(); err != nil {
-				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
+				_, _ = fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
 			}
 
 		case stat := <-t.status:
 			for _, line := range stat.lines {
 				// Ensure that each message ends with exactly one newline.
-				fmt.Fprintln(t.wr, strings.TrimRight(line, "\n"))
+				if _, err := fmt.Fprintln(t.wr, strings.TrimRight(line, "\n")); err != nil {
+					_, _ = fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
+				}
 			}
 			if err := t.wr.Flush(); err != nil {
-				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
+				_, _ = fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
 			}
 		}
 	}
diff --git a/internal/walker/walker.go b/internal/walker/walker.go
index 8acfed2f2..252bc3530 100644
--- a/internal/walker/walker.go
+++ b/internal/walker/walker.go
@@ -28,7 +28,7 @@ type WalkVisitor struct {
 	// was returned. This function is mandatory
 	ProcessNode WalkFunc
 	// Optional callback
-	LeaveDir func(path string)
+	LeaveDir func(path string) error
 }
 
 // Walk calls walkFn recursively for each node in root. If walkFn returns an
@@ -100,7 +100,7 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
 	}
 
 	if visitor.LeaveDir != nil {
-		visitor.LeaveDir(prefix)
+		return visitor.LeaveDir(prefix)
 	}
 
 	return nil


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index bf4e63bb7..5a41723bb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -13,7 +13,7 @@ permissions:
   contents: read
 
 env:
-  latest_go: "1.22.x"
+  latest_go: "1.23.x"
   GO111MODULE: on
 
 jobs:
@@ -23,27 +23,32 @@ jobs:
         # list of jobs to run:
         include:
           - job_name: Windows
-            go: 1.22.x
+            go: 1.23.x
             os: windows-latest
 
           - job_name: macOS
-            go: 1.22.x
+            go: 1.23.x
             os: macOS-latest
             test_fuse: false
 
           - job_name: Linux
-            go: 1.22.x
+            go: 1.23.x
             os: ubuntu-latest
             test_cloud_backends: true
             test_fuse: true
             check_changelog: true
 
           - job_name: Linux (race)
-            go: 1.22.x
+            go: 1.23.x
             os: ubuntu-latest
             test_fuse: true
             test_opts: "-race"
 
+          - job_name: Linux
+            go: 1.22.x
+            os: ubuntu-latest
+            test_fuse: true
+
           - job_name: Linux
             go: 1.21.x
             os: ubuntu-latest
@@ -254,7 +259,7 @@ jobs:
         uses: golangci/golangci-lint-action@v6
         with:
           # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
-          version: v1.57.1
+          version: v1.61.0
           args: --verbose --timeout 5m
 
         # only run golangci-lint for pull requests, otherwise ALL hints get
diff --git a/cmd/restic/cmd_backup_integration_test.go b/cmd/restic/cmd_backup_integration_test.go
index 5926fdd54..4081ef936 100644
--- a/cmd/restic/cmd_backup_integration_test.go
+++ b/cmd/restic/cmd_backup_integration_test.go
@@ -351,12 +351,7 @@ func TestBackupExclude(t *testing.T) {
 	for _, filename := range backupExcludeFilenames {
 		fp := filepath.Join(datadir, filename)
 		rtest.OK(t, os.MkdirAll(filepath.Dir(fp), 0755))
-
-		f, err := os.Create(fp)
-		rtest.OK(t, err)
-
-		fmt.Fprint(f, filename)
-		rtest.OK(t, f.Close())
+		rtest.OK(t, os.WriteFile(fp, []byte(filename), 0o666))
 	}
 
 	snapshots := make(map[string]struct{})
diff --git a/cmd/restic/cmd_backup_test.go b/cmd/restic/cmd_backup_test.go
index 5cbc42436..44e08ff96 100644
--- a/cmd/restic/cmd_backup_test.go
+++ b/cmd/restic/cmd_backup_test.go
@@ -39,21 +39,24 @@ func TestCollectTargets(t *testing.T) {
 	f1, err := os.Create(filepath.Join(dir, "fromfile"))
 	rtest.OK(t, err)
 	// Empty lines should be ignored. A line starting with '#' is a comment.
-	fmt.Fprintf(f1, "\n%s*\n # here's a comment\n", f1.Name())
+	_, err = fmt.Fprintf(f1, "\n%s*\n # here's a comment\n", f1.Name())
+	rtest.OK(t, err)
 	rtest.OK(t, f1.Close())
 
 	f2, err := os.Create(filepath.Join(dir, "fromfile-verbatim"))
 	rtest.OK(t, err)
 	for _, filename := range []string{fooSpace, barStar} {
 		// Empty lines should be ignored. CR+LF is allowed.
-		fmt.Fprintf(f2, "%s\r\n\n", filepath.Join(dir, filename))
+		_, err = fmt.Fprintf(f2, "%s\r\n\n", filepath.Join(dir, filename))
+		rtest.OK(t, err)
 	}
 	rtest.OK(t, f2.Close())
 
 	f3, err := os.Create(filepath.Join(dir, "fromfile-raw"))
 	rtest.OK(t, err)
 	for _, filename := range []string{"baz", "quux"} {
-		fmt.Fprintf(f3, "%s\x00", filepath.Join(dir, filename))
+		_, err = fmt.Fprintf(f3, "%s\x00", filepath.Join(dir, filename))
+		rtest.OK(t, err)
 	}
 	rtest.OK(t, err)
 	rtest.OK(t, f3.Close())
diff --git a/cmd/restic/cmd_ls_test.go b/cmd/restic/cmd_ls_test.go
index b8b074242..3d4e1dbc7 100644
--- a/cmd/restic/cmd_ls_test.go
+++ b/cmd/restic/cmd_ls_test.go
@@ -134,29 +134,29 @@ func TestLsNcdu(t *testing.T) {
 	}
 	modTime := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
 
-	printer.Snapshot(&restic.Snapshot{
+	rtest.OK(t, printer.Snapshot(&restic.Snapshot{
 		Hostname: "host",
 		Paths:    []string{"/example"},
-	})
-	printer.Node("/directory", &restic.Node{
+	}))
+	rtest.OK(t, printer.Node("/directory", &restic.Node{
 		Type:    restic.NodeTypeDir,
 		Name:    "directory",
 		ModTime: modTime,
-	}, false)
-	printer.Node("/directory/data", &restic.Node{
+	}, false))
+	rtest.OK(t, printer.Node("/directory/data", &restic.Node{
 		Type:    restic.NodeTypeFile,
 		Name:    "data",
 		Size:    42,
 		ModTime: modTime,
-	}, false)
-	printer.LeaveDir("/directory")
-	printer.Node("/file", &restic.Node{
+	}, false))
+	rtest.OK(t, printer.LeaveDir("/directory"))
+	rtest.OK(t, printer.Node("/file", &restic.Node{
 		Type:    restic.NodeTypeFile,
 		Name:    "file",
 		Size:    12345,
 		ModTime: modTime,
-	}, false)
-	printer.Close()
+	}, false))
+	rtest.OK(t, printer.Close())
 
 	rtest.Equals(t, `[1, 2, {"time":"0001-01-01T00:00:00Z","tree":null,"paths":["/example"],"hostname":"host"}, [{"name":"/"},
   [
diff --git a/cmd/restic/integration_helpers_unix_test.go b/cmd/restic/integration_helpers_unix_test.go
index df0c4fe63..30852a753 100644
--- a/cmd/restic/integration_helpers_unix_test.go
+++ b/cmd/restic/integration_helpers_unix_test.go
@@ -13,17 +13,17 @@ import (
 
 func (e *dirEntry) equals(out io.Writer, other *dirEntry) bool {
 	if e.path != other.path {
-		fmt.Fprintf(out, "%v: path does not match (%v != %v)\n", e.path, e.path, other.path)
+		_, _ = fmt.Fprintf(out, "%v: path does not match (%v != %v)\n", e.path, e.path, other.path)
 		return false
 	}
 
 	if e.fi.Mode() != other.fi.Mode() {
-		fmt.Fprintf(out, "%v: mode does not match (%v != %v)\n", e.path, e.fi.Mode(), other.fi.Mode())
+		_, _ = fmt.Fprintf(out, "%v: mode does not match (%v != %v)\n", e.path, e.fi.Mode(), other.fi.Mode())
 		return false
 	}
 
 	if !sameModTime(e.fi, other.fi) {
-		fmt.Fprintf(out, "%v: ModTime does not match (%v != %v)\n", e.path, e.fi.ModTime(), other.fi.ModTime())
+		_, _ = fmt.Fprintf(out, "%v: ModTime does not match (%v != %v)\n", e.path, e.fi.ModTime(), other.fi.ModTime())
 		return false
 	}
 
@@ -31,17 +31,17 @@ func (e *dirEntry) equals(out io.Writer, other *dirEntry) bool {
 	stat2, _ := other.fi.Sys().(*syscall.Stat_t)
 
 	if stat.Uid != stat2.Uid {
-		fmt.Fprintf(out, "%v: UID does not match (%v != %v)\n", e.path, stat.Uid, stat2.Uid)
+		_, _ = fmt.Fprintf(out, "%v: UID does not match (%v != %v)\n", e.path, stat.Uid, stat2.Uid)
 		return false
 	}
 
 	if stat.Gid != stat2.Gid {
-		fmt.Fprintf(out, "%v: GID does not match (%v != %v)\n", e.path, stat.Gid, stat2.Gid)
+		_, _ = fmt.Fprintf(out, "%v: GID does not match (%v != %v)\n", e.path, stat.Gid, stat2.Gid)
 		return false
 	}
 
 	if stat.Nlink != stat2.Nlink {
-		fmt.Fprintf(out, "%v: Number of links do not match (%v != %v)\n", e.path, stat.Nlink, stat2.Nlink)
+		_, _ = fmt.Fprintf(out, "%v: Number of links do not match (%v != %v)\n", e.path, stat.Nlink, stat2.Nlink)
 		return false
 	}
 
diff --git a/internal/backend/rest/rest_test.go b/internal/backend/rest/rest_test.go
index 891f60a87..50560f66d 100644
--- a/internal/backend/rest/rest_test.go
+++ b/internal/backend/rest/rest_test.go
@@ -106,7 +106,7 @@ func runRESTServer(ctx context.Context, t testing.TB, dir, reqListenAddr string)
 					matched = true
 				}
 			}
-			fmt.Fprintln(os.Stdout, line) // print all output to console
+			_, _ = fmt.Fprintln(os.Stdout, line) // print all output to console
 		}
 	}()
 
diff --git a/internal/walker/walker_test.go b/internal/walker/walker_test.go
index fa377bb8f..3614a2397 100644
--- a/internal/walker/walker_test.go
+++ b/internal/walker/walker_test.go
@@ -93,12 +93,12 @@ func (t TreeMap) Connections() uint {
 
 // checkFunc returns a function suitable for walking the tree to check
 // something, and a function which will check the final result.
-type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string), final func(testing.TB))
+type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB))
 
 // checkItemOrder ensures that the order of the 'path' arguments is the one passed in as 'want'.
 func checkItemOrder(want []string) checkFunc {
 	pos := 0
-	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string), final func(testing.TB)) {
+	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
 		walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
 			if err != nil {
 				t.Errorf("error walking %v: %v", path, err)
@@ -117,8 +117,8 @@ func checkItemOrder(want []string) checkFunc {
 			return nil
 		}
 
-		leaveDir = func(path string) {
-			_ = walker(restic.ID{}, "leave: "+path, nil, nil)
+		leaveDir = func(path string) error {
+			return walker(restic.ID{}, "leave: "+path, nil, nil)
 		}
 
 		final = func(t testing.TB) {
@@ -134,7 +134,7 @@ func checkItemOrder(want []string) checkFunc {
 // checkParentTreeOrder ensures that the order of the 'parentID' arguments is the one passed in as 'want'.
 func checkParentTreeOrder(want []string) checkFunc {
 	pos := 0
-	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string), final func(testing.TB)) {
+	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
 		walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
 			if err != nil {
 				t.Errorf("error walking %v: %v", path, err)
@@ -168,7 +168,7 @@ func checkParentTreeOrder(want []string) checkFunc {
 func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
 	var pos int
 
-	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string), final func(testing.TB)) {
+	return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
 		walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
 			if err != nil {
 				t.Errorf("error walking %v: %v", path, err)
@@ -192,8 +192,8 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
 			return nil
 		}
 
-		leaveDir = func(path string) {
-			_ = walker(restic.ID{}, "leave: "+path, nil, nil)
+		leaveDir = func(path string) error {
+			return walker(restic.ID{}, "leave: "+path, nil, nil)
 		}
 
 		final = func(t testing.TB) {