From 5ca6022d007bc83daba1e7297480d37f56554455 Mon Sep 17 00:00:00 2001
From: Moritz Poldrack <git@moritz.sh>
Date: Fri, 29 Jul 2022 22:31:54 +0200
Subject: [PATCH] lint: ensure errors are at least logged (errcheck)

Signed-off-by: Moritz Poldrack <moritz@poldrack.dev>
Acked-by: Robin Jarry <robin@jarry.cc>
---
 aerc.go                          | 15 ++++++--
 commands/account/export-mbox.go  |  5 ++-
 commands/msg/forward.go          | 14 +++++--
 commands/msg/invite.go           | 11 +++++-
 commands/msg/pipe.go             | 13 +++++--
 commands/msg/recall.go           |  5 ++-
 commands/msg/reply.go            | 11 +++++-
 completer/completer.go           |  2 +-
 config/config.go                 |  5 ++-
 lib/calendar/calendar.go         |  5 ++-
 lib/crypto/gpg/gpgbin/encrypt.go |  9 +++--
 lib/crypto/gpg/gpgbin/gpgbin.go  | 12 +++++-
 lib/crypto/gpg/gpgbin/keys.go    |  5 ++-
 lib/crypto/gpg/gpgbin/sign.go    |  9 +++--
 lib/crypto/gpg/gpgbin/verify.go  | 13 +++++--
 lib/crypto/gpg/reader.go         |  8 ++--
 lib/crypto/gpg/writer.go         | 19 +++++++---
 lib/crypto/pgp/pgp.go            |  7 +++-
 lib/msgstore.go                  |  4 +-
 lib/socket.go                    | 47 +++++++++++++++++-------
 lib/threadbuilder.go             |  2 +-
 logging/logger.go                |  8 ++--
 logging/panic-logger.go          |  2 +-
 widgets/account-wizard.go        | 13 ++++---
 widgets/compose.go               | 63 +++++++++++++++++++++++++-------
 widgets/dirtree.go               | 11 +++++-
 widgets/msglist.go               |  6 ++-
 widgets/msgviewer.go             | 40 ++++++++++++++++----
 widgets/terminal.go              | 15 ++++++--
 worker/imap/open.go              |  2 +-
 worker/mbox/models.go            |  5 ++-
 worker/notmuch/lib/database.go   | 16 ++++++--
 worker/types/thread.go           |  2 +-
 33 files changed, 301 insertions(+), 103 deletions(-)

diff --git a/aerc.go b/aerc.go
index cd8c4da..9ce704b 100644
--- a/aerc.go
+++ b/aerc.go
@@ -164,7 +164,10 @@ func main() {
 	deferLoop := make(chan struct{})
 
 	c := crypto.New(conf.General.PgpProvider)
-	c.Init()
+	err = c.Init()
+	if err != nil {
+		logging.Warnf("failed to initialise crypto interface: %v", err)
+	}
 	defer c.Close()
 
 	aerc = widgets.NewAerc(conf, c, func(cmd []string) error {
@@ -205,7 +208,10 @@ func main() {
 		err := lib.ConnectAndExec(arg)
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "Failed to communicate to aerc: %v\n", err)
-			aerc.CloseBackends()
+			err = aerc.CloseBackends()
+			if err != nil {
+				logging.Warnf("failed to close backends: %v", err)
+			}
 			return
 		}
 	}
@@ -223,5 +229,8 @@ func main() {
 			time.Sleep(16 * time.Millisecond)
 		}
 	}
-	aerc.CloseBackends()
+	err = aerc.CloseBackends()
+	if err != nil {
+		logging.Warnf("failed to close backends: %v", err)
+	}
 }
diff --git a/commands/account/export-mbox.go b/commands/account/export-mbox.go
index 3e86fc1..b68e1cc 100644
--- a/commands/account/export-mbox.go
+++ b/commands/account/export-mbox.go
@@ -96,7 +96,10 @@ func (ExportMbox) Execute(aerc *widgets.Aerc, args []string) error {
 					done <- false
 				case *types.FullMessage:
 					mu.Lock()
-					mboxer.Write(file, msg.Content.Reader, "", t)
+					err := mboxer.Write(file, msg.Content.Reader, "", t)
+					if err != nil {
+						logging.Warnf("failed to write mbox: %v", err)
+					}
 					for i, uid := range uids {
 						if uid == msg.Content.Uid {
 							uids = append(uids[:i], uids[i+1:]...)
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index cd62417..ddc2791 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -133,14 +133,20 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error {
 		store.FetchFull([]uint32{msg.Uid}, func(fm *types.FullMessage) {
 			tmpFile, err := os.Create(tmpFileName)
 			if err != nil {
-				println(err)
-				// TODO: Do something with the error
-				addTab()
+				logging.Warnf("failed to create temporary attachment: %v", err)
+				_, err = addTab()
+				if err != nil {
+					logging.Warnf("failed to add tab: %v", err)
+				}
 				return
 			}
 
 			defer tmpFile.Close()
-			io.Copy(tmpFile, fm.Content.Reader)
+			_, err = io.Copy(tmpFile, fm.Content.Reader)
+			if err != nil {
+				logging.Warnf("failed to write to tmpfile: %w", err)
+				return
+			}
 			composer, err := addTab()
 			if err != nil {
 				return
diff --git a/commands/msg/invite.go b/commands/msg/invite.go
index 936cea7..4e8d01d 100644
--- a/commands/msg/invite.go
+++ b/commands/msg/invite.go
@@ -8,6 +8,7 @@ import (
 	"git.sr.ht/~rjarry/aerc/lib"
 	"git.sr.ht/~rjarry/aerc/lib/calendar"
 	"git.sr.ht/~rjarry/aerc/lib/format"
+	"git.sr.ht/~rjarry/aerc/logging"
 	"git.sr.ht/~rjarry/aerc/models"
 	"git.sr.ht/~rjarry/aerc/widgets"
 	"github.com/emersion/go-message/mail"
@@ -153,7 +154,10 @@ func (invite) Execute(aerc *widgets.Aerc, args []string) error {
 		}
 
 		composer.SetContents(cr.PlainText)
-		composer.AppendPart(cr.MimeType, cr.Params, cr.CalendarText)
+		err = composer.AppendPart(cr.MimeType, cr.Params, cr.CalendarText)
+		if err != nil {
+			return fmt.Errorf("failed to write invitation: %w", err)
+		}
 		composer.FocusTerminal()
 
 		tab := aerc.NewTab(composer, subject)
@@ -180,7 +184,10 @@ func (invite) Execute(aerc *widgets.Aerc, args []string) error {
 			aerc.PushError(err.Error())
 			return
 		} else {
-			addTab(cr)
+			err := addTab(cr)
+			if err != nil {
+				logging.Warnf("failed to add tab: %v", err)
+			}
 		}
 	})
 	return nil
diff --git a/commands/msg/pipe.go b/commands/msg/pipe.go
index 176d1f0..7a2489a 100644
--- a/commands/msg/pipe.go
+++ b/commands/msg/pipe.go
@@ -96,7 +96,10 @@ func (Pipe) Execute(aerc *widgets.Aerc, args []string) error {
 			defer logging.PanicHandler()
 
 			defer pipe.Close()
-			io.Copy(pipe, reader)
+			_, err := io.Copy(pipe, reader)
+			if err != nil {
+				logging.Errorf("failed to send data to pipe: %w", err)
+			}
 		}()
 		err = ecmd.Run()
 		if err != nil {
@@ -224,10 +227,14 @@ func newMessagesReader(messages []*types.FullMessage, useMbox bool) io.Reader {
 	go func() {
 		defer pw.Close()
 		for _, msg := range messages {
+			var err error
 			if useMbox {
-				mboxer.Write(pw, msg.Content.Reader, "", time.Now())
+				err = mboxer.Write(pw, msg.Content.Reader, "", time.Now())
 			} else {
-				io.Copy(pw, msg.Content.Reader)
+				_, err = io.Copy(pw, msg.Content.Reader)
+			}
+			if err != nil {
+				logging.Warnf("failed to write data: %v", err)
 			}
 		}
 	}()
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index f0bafc7..8434b8d 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -182,7 +182,10 @@ func (Recall) Execute(aerc *widgets.Aerc, args []string) error {
 						composer.SetEncrypt(md.IsEncrypted)
 					}
 					if md.IsSigned {
-						composer.SetSign(md.IsSigned)
+						err = composer.SetSign(md.IsSigned)
+						if err != nil {
+							logging.Warnf("failed to set signed state: %v", err)
+						}
 					}
 				}
 				addTab()
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index dd5aa19..8365172 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -12,6 +12,7 @@ import (
 
 	"git.sr.ht/~rjarry/aerc/lib"
 	"git.sr.ht/~rjarry/aerc/lib/format"
+	"git.sr.ht/~rjarry/aerc/logging"
 	"git.sr.ht/~rjarry/aerc/models"
 	"git.sr.ht/~rjarry/aerc/widgets"
 	"github.com/emersion/go-message/mail"
@@ -224,9 +225,15 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error {
 
 		store.FetchBodyPart(msg.Uid, part, func(reader io.Reader) {
 			buf := new(bytes.Buffer)
-			buf.ReadFrom(reader)
+			_, err := buf.ReadFrom(reader)
+			if err != nil {
+				logging.Warnf("failed to fetch bodypart: %v", err)
+			}
 			original.Text = buf.String()
-			addTab()
+			err = addTab()
+			if err != nil {
+				logging.Warnf("failed to add tab: %v", err)
+			}
 		})
 		return nil
 	} else {
diff --git a/completer/completer.go b/completer/completer.go
index 83a9c12..4a65ca7 100644
--- a/completer/completer.go
+++ b/completer/completer.go
@@ -95,7 +95,7 @@ func (c *Completer) completeAddress(s string) ([]string, string, error) {
 	// programs will do to signal no matches. We don't want to spam the user with
 	// spurious error messages, so we'll ignore any errors that arise at this
 	// point.
-	defer cmd.Wait()
+	defer cmd.Wait() //nolint:errcheck // see above
 
 	completions, err := readCompletions(stdout)
 	if err != nil {
diff --git a/config/config.go b/config/config.go
index 249fa27..e87dccf 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1042,7 +1042,10 @@ func (config AercConfig) mergeContextualUi(baseUi UIConfig,
 			continue
 		}
 
-		mergo.Merge(&baseUi, contextualUi.UiConfig, mergo.WithOverride)
+		err := mergo.Merge(&baseUi, contextualUi.UiConfig, mergo.WithOverride)
+		if err != nil {
+			logging.Warnf("merge ui failed: %v", err)
+		}
 		if contextualUi.UiConfig.StyleSetName != "" {
 			baseUi.style = contextualUi.UiConfig.style
 		}
diff --git a/lib/calendar/calendar.go b/lib/calendar/calendar.go
index ebe1b77..c15e035 100644
--- a/lib/calendar/calendar.go
+++ b/lib/calendar/calendar.go
@@ -119,7 +119,10 @@ type calendar struct {
 func parse(reader io.Reader) (*calendar, error) {
 	// fix capitalized mailto for parsing of ics file
 	var sb strings.Builder
-	io.Copy(&sb, reader)
+	_, err := io.Copy(&sb, reader)
+	if err != nil {
+		return nil, fmt.Errorf("failed to copy calendar data: %w", err)
+	}
 	re := regexp.MustCompile("MAILTO:(.+@)")
 	str := re.ReplaceAllString(sb.String(), "mailto:${1}")
 
diff --git a/lib/crypto/gpg/gpgbin/encrypt.go b/lib/crypto/gpg/gpgbin/encrypt.go
index 31245a7..9ca4526 100644
--- a/lib/crypto/gpg/gpgbin/encrypt.go
+++ b/lib/crypto/gpg/gpgbin/encrypt.go
@@ -25,15 +25,18 @@ func Encrypt(r io.Reader, to []string, from string) ([]byte, error) {
 	args = append(args, "--encrypt", "-")
 
 	g := newGpg(r, args)
-	g.cmd.Run()
+	err := g.cmd.Run()
+	if err != nil {
+		return nil, fmt.Errorf("gpg: failed to run encryption: %w", err)
+	}
 	outRdr := bytes.NewReader(g.stdout.Bytes())
 	var md models.MessageDetails
-	err := parse(outRdr, &md)
+	err = parse(outRdr, &md)
 	if err != nil {
 		return nil, fmt.Errorf("gpg: failure to encrypt: %v. check public key(s)", err)
 	}
 	var buf bytes.Buffer
-	io.Copy(&buf, md.Body)
+	_, _ = io.Copy(&buf, md.Body)
 
 	return buf.Bytes(), nil
 }
diff --git a/lib/crypto/gpg/gpgbin/gpgbin.go b/lib/crypto/gpg/gpgbin/gpgbin.go
index 2eb83dc..1e8c7dc 100644
--- a/lib/crypto/gpg/gpgbin/gpgbin.go
+++ b/lib/crypto/gpg/gpgbin/gpgbin.go
@@ -63,7 +63,11 @@ func getIdentity(key uint64) string {
 
 	var outbuf strings.Builder
 	cmd.Stdout = &outbuf
-	cmd.Run()
+	err := cmd.Run()
+	if err != nil {
+		logging.Errorf("gpg: failed to get identity: %v", err)
+		return ""
+	}
 	out := strings.Split(outbuf.String(), "\n")
 	for _, line := range out {
 		if strings.HasPrefix(line, "uid") {
@@ -85,7 +89,11 @@ func getKeyId(s string, private bool) string {
 
 	var outbuf strings.Builder
 	cmd.Stdout = &outbuf
-	cmd.Run()
+	err := cmd.Run()
+	if err != nil {
+		logging.Errorf("gpg: failed to get key ID: %v", err)
+		return ""
+	}
 	out := strings.Split(outbuf.String(), "\n")
 	for _, line := range out {
 		if strings.HasPrefix(line, "fpr") {
diff --git a/lib/crypto/gpg/gpgbin/keys.go b/lib/crypto/gpg/gpgbin/keys.go
index bef90cf..ca166e9 100644
--- a/lib/crypto/gpg/gpgbin/keys.go
+++ b/lib/crypto/gpg/gpgbin/keys.go
@@ -36,7 +36,10 @@ func ExportPublicKey(k string) (io.Reader, error) {
 	var stderr strings.Builder
 	cmd.Stdout = &outbuf
 	cmd.Stderr = &stderr
-	cmd.Run()
+	err := cmd.Run()
+	if err != nil {
+		return nil, fmt.Errorf("gpg: export failed: %w", err)
+	}
 	if strings.Contains(stderr.String(), "gpg") {
 		return nil, fmt.Errorf("gpg: error exporting key")
 	}
diff --git a/lib/crypto/gpg/gpgbin/sign.go b/lib/crypto/gpg/gpgbin/sign.go
index 31fd2b5..caf31d1 100644
--- a/lib/crypto/gpg/gpgbin/sign.go
+++ b/lib/crypto/gpg/gpgbin/sign.go
@@ -17,15 +17,18 @@ func Sign(r io.Reader, from string) ([]byte, string, error) {
 	}
 
 	g := newGpg(r, args)
-	g.cmd.Run()
+	err := g.cmd.Run()
+	if err != nil {
+		return nil, "", fmt.Errorf("failed to run signing: %w", err)
+	}
 
 	outRdr := bytes.NewReader(g.stdout.Bytes())
 	var md models.MessageDetails
-	err := parse(outRdr, &md)
+	err = parse(outRdr, &md)
 	if err != nil {
 		return nil, "", fmt.Errorf("failed to parse messagedetails: %v", err)
 	}
 	var buf bytes.Buffer
-	io.Copy(&buf, md.Body)
+	_, _ = io.Copy(&buf, md.Body)
 	return buf.Bytes(), md.Micalg, nil
 }
diff --git a/lib/crypto/gpg/gpgbin/verify.go b/lib/crypto/gpg/gpgbin/verify.go
index be9f26f..e593084 100644
--- a/lib/crypto/gpg/gpgbin/verify.go
+++ b/lib/crypto/gpg/gpgbin/verify.go
@@ -2,6 +2,7 @@ package gpgbin
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
@@ -19,7 +20,7 @@ func Verify(m io.Reader, s io.Reader) (*models.MessageDetails, error) {
 		if err != nil {
 			return nil, err
 		}
-		io.Copy(sig, s)
+		_, _ = io.Copy(sig, s)
 		sig.Close()
 		defer os.Remove(sig.Name())
 		args = append(args, sig.Name(), "-")
@@ -29,11 +30,17 @@ func Verify(m io.Reader, s io.Reader) (*models.MessageDetails, error) {
 		return nil, err
 	}
 	g := newGpg(bytes.NewReader(orig), args)
-	g.cmd.Run()
+	err = g.cmd.Run()
+	if err != nil {
+		return nil, fmt.Errorf("gpg: failed to run verification: %w", err)
+	}
 
 	out := bytes.NewReader(g.stdout.Bytes())
 	md := new(models.MessageDetails)
-	parse(out, md)
+	err = parse(out, md)
+	if err != nil {
+		return nil, fmt.Errorf("gpg: failed to parse result: %w", err)
+	}
 
 	md.Body = bytes.NewReader(orig)
 
diff --git a/lib/crypto/gpg/reader.go b/lib/crypto/gpg/reader.go
index bf977ed..40da82f 100644
--- a/lib/crypto/gpg/reader.go
+++ b/lib/crypto/gpg/reader.go
@@ -38,7 +38,7 @@ func NewReader(h textproto.Header, body io.Reader) (*Reader, error) {
 	}
 
 	var headerBuf bytes.Buffer
-	textproto.WriteHeader(&headerBuf, h)
+	_ = textproto.WriteHeader(&headerBuf, h)
 
 	return &Reader{
 		Header: h,
@@ -123,7 +123,7 @@ func newEncryptedReader(h textproto.Header, mr *textproto.MultipartReader) (*Rea
 	}
 
 	var headerBuf bytes.Buffer
-	textproto.WriteHeader(&headerBuf, cleartextHeader)
+	_ = textproto.WriteHeader(&headerBuf, cleartextHeader)
 	md.Body = io.MultiReader(&headerBuf, cleartext)
 
 	return &Reader{
@@ -139,11 +139,11 @@ func newSignedReader(h textproto.Header, mr *textproto.MultipartReader, micalg s
 		return nil, fmt.Errorf("gpgmail: failed to read signed part in multipart/signed message: %v", err)
 	}
 	var headerBuf bytes.Buffer
-	textproto.WriteHeader(&headerBuf, p.Header)
+	_ = textproto.WriteHeader(&headerBuf, p.Header)
 	var msg bytes.Buffer
 	headerRdr := bytes.NewReader(headerBuf.Bytes())
 	fullMsg := io.MultiReader(headerRdr, p)
-	io.Copy(&msg, fullMsg)
+	_, _ = io.Copy(&msg, fullMsg)
 
 	sig, err := mr.NextPart()
 	if err != nil {
diff --git a/lib/crypto/gpg/writer.go b/lib/crypto/gpg/writer.go
index 96d083e..0648ec2 100644
--- a/lib/crypto/gpg/writer.go
+++ b/lib/crypto/gpg/writer.go
@@ -30,7 +30,10 @@ func (es *EncrypterSigner) Close() (err error) {
 	if err != nil {
 		return err
 	}
-	es.encryptedWriter.Write(enc)
+	_, err = es.encryptedWriter.Write(enc)
+	if err != nil {
+		return fmt.Errorf("gpg: failed to write encrypted writer: %w", err)
+	}
 	return nil
 }
 
@@ -65,8 +68,8 @@ func (s *Signer) Close() (err error) {
 	}
 	boundary := s.mw.Boundary()
 	fmt.Fprintf(s.w, "--%s\r\n", boundary)
-	s.w.Write(s.signedMsg.Bytes())
-	s.w.Write([]byte("\r\n"))
+	_, _ = s.w.Write(s.signedMsg.Bytes())
+	_, _ = s.w.Write([]byte("\r\n"))
 
 	var signedHeader textproto.Header
 	signedHeader.Set("Content-Type", "application/pgp-signature; name=\"signature.asc\"")
@@ -100,7 +103,10 @@ func Encrypt(w io.Writer, h textproto.Header, rcpts []string, from string) (io.W
 	mw := textproto.NewMultipartWriter(w)
 
 	if forceBoundary != "" {
-		mw.SetBoundary(forceBoundary)
+		err := mw.SetBoundary(forceBoundary)
+		if err != nil {
+			return nil, fmt.Errorf("gpg: failed to set boundary: %w", err)
+		}
 	}
 
 	params := map[string]string{
@@ -154,7 +160,10 @@ func Sign(w io.Writer, h textproto.Header, from string) (io.WriteCloser, error)
 	mw := textproto.NewMultipartWriter(w)
 
 	if forceBoundary != "" {
-		mw.SetBoundary(forceBoundary)
+		err := mw.SetBoundary(forceBoundary)
+		if err != nil {
+			return nil, fmt.Errorf("gpg: failed to set boundary: %w", err)
+		}
 	}
 
 	var msg bytes.Buffer
diff --git a/lib/crypto/pgp/pgp.go b/lib/crypto/pgp/pgp.go
index c305f06..579395f 100644
--- a/lib/crypto/pgp/pgp.go
+++ b/lib/crypto/pgp/pgp.go
@@ -31,7 +31,10 @@ var (
 
 func (m *Mail) Init() error {
 	logging.Infof("Initializing PGP keyring")
-	os.MkdirAll(path.Join(xdg.DataHome(), "aerc"), 0o700)
+	err := os.MkdirAll(path.Join(xdg.DataHome(), "aerc"), 0o700)
+	if err != nil {
+		return fmt.Errorf("failed to create data directory: %v", err)
+	}
 
 	lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock")
 	lockfile, err := os.OpenFile(lockpath, os.O_CREATE|os.O_EXCL, 0o600)
@@ -294,7 +297,7 @@ func (m *Mail) ExportKey(k string) (io.Reader, error) {
 	if err != nil {
 		return nil, fmt.Errorf("pgp: error exporting key: %v", err)
 	}
-	w.Write(pks.Bytes())
+	_, err = w.Write(pks.Bytes())
 	if err != nil {
 		return nil, fmt.Errorf("pgp: error exporting key: %v", err)
 	}
diff --git a/lib/msgstore.go b/lib/msgstore.go
index ec15d42..fd67c79 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -225,7 +225,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 		newMap := make(map[uint32]*models.MessageInfo)
 
 		for i := len(msg.Threads) - 1; i >= 0; i-- {
-			msg.Threads[i].Walk(func(t *types.Thread, level int, currentErr error) error {
+			_ = msg.Threads[i].Walk(func(t *types.Thread, level int, currentErr error) error {
 				uid := t.Uid
 				uids = append([]uint32{uid}, uids...)
 				if msg, ok := store.Messages[uid]; ok {
@@ -316,7 +316,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 		store.results = newResults
 
 		for _, thread := range store.Threads() {
-			thread.Walk(func(t *types.Thread, _ int, _ error) error {
+			_ = thread.Walk(func(t *types.Thread, _ int, _ error) error {
 				if _, deleted := toDelete[t.Uid]; deleted {
 					t.Deleted = true
 				}
diff --git a/lib/socket.go b/lib/socket.go
index fa36172..c83cc8a 100644
--- a/lib/socket.go
+++ b/lib/socket.go
@@ -64,40 +64,56 @@ func (as *AercServer) handleClient(conn net.Conn) {
 	clientId := atomic.AddInt64(&lastId, 1)
 	logging.Debugf("unix:%d accepted connection", clientId)
 	scanner := bufio.NewScanner(conn)
-	conn.SetDeadline(time.Now().Add(1 * time.Minute))
+	err := conn.SetDeadline(time.Now().Add(1 * time.Minute))
+	if err != nil {
+		logging.Errorf("failed to set deadline: %v", err)
+	}
 	for scanner.Scan() {
-		conn.SetDeadline(time.Now().Add(1 * time.Minute))
+		err = conn.SetDeadline(time.Now().Add(1 * time.Minute))
+		if err != nil {
+			logging.Errorf("failed to update deadline: %v", err)
+		}
 		msg := scanner.Text()
 		logging.Debugf("unix:%d got message %s", clientId, msg)
 		if !strings.ContainsRune(msg, ':') {
-			conn.Write([]byte("error: invalid command\n"))
+			_, innererr := conn.Write([]byte("error: invalid command\n"))
+			if innererr != nil {
+				logging.Errorf("failed to write error message: %v", innererr)
+			}
 			continue
 		}
 		prefix := msg[:strings.IndexRune(msg, ':')]
+		var err error
 		switch prefix {
 		case "mailto":
 			mailto, err := url.Parse(msg)
 			if err != nil {
-				conn.Write([]byte(fmt.Sprintf("error: %v\n", err)))
+				_, innererr := conn.Write([]byte(fmt.Sprintf("error: %v\n", err)))
+				if innererr != nil {
+					logging.Errorf("failed to write error message: %v", innererr)
+				}
 				break
 			}
 			if as.OnMailto != nil {
 				err = as.OnMailto(mailto)
-			}
-			if err != nil {
-				conn.Write([]byte(fmt.Sprintf("result: %v\n", err)))
-			} else {
-				conn.Write([]byte("result: success\n"))
+				if err != nil {
+					logging.Errorf("mailto failed: %v", err)
+				}
 			}
 		case "mbox":
-			var err error
 			if as.OnMbox != nil {
 				err = as.OnMbox(msg)
 			}
+		}
+		if err != nil {
+			_, err = conn.Write([]byte(fmt.Sprintf("result: %v\n", err)))
 			if err != nil {
-				conn.Write([]byte(fmt.Sprintf("result: %v\n", err)))
-			} else {
-				conn.Write([]byte("result: success\n"))
+				logging.Errorf("failed to send error: %v")
+			}
+		} else {
+			_, err = conn.Write([]byte("result: success\n"))
+			if err != nil {
+				logging.Errorf("failed to send successmessage: %v")
 			}
 		}
 	}
@@ -110,7 +126,10 @@ func ConnectAndExec(msg string) error {
 	if err != nil {
 		return err
 	}
-	conn.Write([]byte(msg + "\n"))
+	_, err = conn.Write([]byte(msg + "\n"))
+	if err != nil {
+		return fmt.Errorf("failed to send message: %w", err)
+	}
 	scanner := bufio.NewScanner(conn)
 	if !scanner.Scan() {
 		return errors.New("No response from server")
diff --git a/lib/threadbuilder.go b/lib/threadbuilder.go
index ce56afe..6cd98e8 100644
--- a/lib/threadbuilder.go
+++ b/lib/threadbuilder.go
@@ -155,7 +155,7 @@ func (builder *ThreadBuilder) sortThreads(threads []*types.Thread, orderedUids [
 func (builder *ThreadBuilder) RebuildUids(threads []*types.Thread) {
 	uids := make([]uint32, 0, len(threads))
 	for i := len(threads) - 1; i >= 0; i-- {
-		threads[i].Walk(func(t *types.Thread, level int, currentErr error) error {
+		_ = threads[i].Walk(func(t *types.Thread, level int, currentErr error) error {
 			uids = append(uids, t.Uid)
 			return nil
 		})
diff --git a/logging/logger.go b/logging/logger.go
index 0ecc35c..cf7192e 100644
--- a/logging/logger.go
+++ b/logging/logger.go
@@ -36,7 +36,7 @@ func Debugf(message string, args ...interface{}) {
 	if len(args) > 0 {
 		message = fmt.Sprintf(message, args...)
 	}
-	dbg.Output(2, message)
+	dbg.Output(2, message) //nolint:errcheck // we can't do anything with what we log
 }
 
 func Infof(message string, args ...interface{}) {
@@ -46,7 +46,7 @@ func Infof(message string, args ...interface{}) {
 	if len(args) > 0 {
 		message = fmt.Sprintf(message, args...)
 	}
-	info.Output(2, message)
+	info.Output(2, message) //nolint:errcheck // we can't do anything with what we log
 }
 
 func Warnf(message string, args ...interface{}) {
@@ -56,7 +56,7 @@ func Warnf(message string, args ...interface{}) {
 	if len(args) > 0 {
 		message = fmt.Sprintf(message, args...)
 	}
-	warn.Output(2, message)
+	warn.Output(2, message) //nolint:errcheck // we can't do anything with what we log
 }
 
 func Errorf(message string, args ...interface{}) {
@@ -66,5 +66,5 @@ func Errorf(message string, args ...interface{}) {
 	if len(args) > 0 {
 		message = fmt.Sprintf(message, args...)
 	}
-	err.Output(2, message)
+	err.Output(2, message) //nolint:errcheck // we can't do anything with what we log
 }
diff --git a/logging/panic-logger.go b/logging/panic-logger.go
index b80e6cf..9f57f65 100644
--- a/logging/panic-logger.go
+++ b/logging/panic-logger.go
@@ -42,7 +42,7 @@ func PanicHandler() {
 	fmt.Fprintln(panicLog, strings.Repeat("#", 80))
 	fmt.Fprintf(outputs, "%s\n", panicMessage)
 	fmt.Fprintf(panicLog, "Error: %v\n\n", r)
-	panicLog.Write(debug.Stack())
+	panicLog.Write(debug.Stack()) //nolint:errcheck // we are already in a panic, so not much we can do here
 	fmt.Fprintf(os.Stderr, "\nThis error was also written to: %s\n", filename)
 	panic(r)
 }
diff --git a/widgets/account-wizard.go b/widgets/account-wizard.go
index 9ddecfb..5ed39d5 100644
--- a/widgets/account-wizard.go
+++ b/widgets/account-wizard.go
@@ -493,16 +493,17 @@ func (wizard *AccountWizard) finish(tutorial bool) {
 		return
 	}
 	sec, _ = file.NewSection(wizard.accountName.String())
-	sec.NewKey("source", wizard.imapUrl.String())
-	sec.NewKey("outgoing", wizard.smtpUrl.String())
-	sec.NewKey("default", "INBOX")
+	// these can't really fail
+	sec.NewKey("source", wizard.imapUrl.String())   //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
+	sec.NewKey("outgoing", wizard.smtpUrl.String()) //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
+	sec.NewKey("default", "INBOX")                  //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
 	if wizard.smtpMode == SMTP_STARTTLS {
-		sec.NewKey("smtp-starttls", "yes")
+		sec.NewKey("smtp-starttls", "yes") //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
 	}
-	sec.NewKey("from", fmt.Sprintf("%s <%s>",
+	sec.NewKey("from", fmt.Sprintf("%s <%s>", //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
 		wizard.fullName.String(), wizard.email.String()))
 	if wizard.copySent {
-		sec.NewKey("copy-to", "Sent")
+		sec.NewKey("copy-to", "Sent") //nolint:errcheck // can't fail. option shadowing is not enabled and the key is not empty
 	}
 
 	if !wizard.temporary {
diff --git a/widgets/compose.go b/widgets/compose.go
index 5e8a808..b2a5e05 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -119,11 +119,15 @@ func NewComposer(aerc *Aerc, acct *AccountView, conf *config.AercConfig,
 	c.AddSignature()
 
 	c.updateGrid()
-	c.updateCrypto()
+	err = c.updateCrypto()
+	if err != nil {
+		logging.Warnf("failed to update crypto: %v", err)
+	}
 	c.ShowTerminal()
 
 	if c.acctConfig.PgpAutoSign {
-		c.SetSign(true)
+		err = c.SetSign(true)
+		logging.Warnf("failed to enable message signing: %v", err)
 	}
 	if c.acctConfig.PgpOpportunisticEncrypt {
 		c.SetEncrypt(true)
@@ -196,7 +200,10 @@ func (c *Composer) SetAttachKey(attach bool) error {
 			}
 		}
 		if found {
-			c.DeleteAttachment(name)
+			err := c.DeleteAttachment(name)
+			if err != nil {
+				return fmt.Errorf("failed to delete attachment '%s: %v", name, err)
+			}
 		} else {
 			attach = !attach
 		}
@@ -264,7 +271,10 @@ func (c *Composer) Sign() bool {
 func (c *Composer) SetEncrypt(encrypt bool) *Composer {
 	if !encrypt {
 		c.encrypt = encrypt
-		c.updateCrypto()
+		err := c.updateCrypto()
+		if err != nil {
+			logging.Warnf("failed to update crypto: %v", err)
+		}
 		return c
 	}
 	// Check on any attempt to encrypt, and any lost focus of "to", "cc", or
@@ -337,17 +347,38 @@ func (c *Composer) updateCrypto() error {
 // Note: this does not reload the editor. You must call this before the first
 // Draw() call.
 func (c *Composer) SetContents(reader io.Reader) *Composer {
-	c.email.Seek(0, io.SeekStart)
-	io.Copy(c.email, reader)
-	c.email.Sync()
-	c.email.Seek(0, io.SeekStart)
+	_, err := c.email.Seek(0, io.SeekStart)
+	if err != nil {
+		logging.Warnf("failed to seek beginning of mail: %v", err)
+	}
+	_, err = io.Copy(c.email, reader)
+	if err != nil {
+		logging.Warnf("failed to copy mail: %v", err)
+	}
+	err = c.email.Sync()
+	if err != nil {
+		logging.Warnf("failed to sync mail: %v", err)
+	}
+	_, err = c.email.Seek(0, io.SeekStart)
+	if err != nil {
+		logging.Warnf("failed to seek beginning of mail after sync: %v", err)
+	}
 	return c
 }
 
 func (c *Composer) AppendContents(reader io.Reader) {
-	c.email.Seek(0, io.SeekEnd)
-	io.Copy(c.email, reader)
-	c.email.Sync()
+	_, err := c.email.Seek(0, io.SeekEnd)
+	if err != nil {
+		logging.Warnf("failed to seek beginning of mail: %v", err)
+	}
+	_, err = io.Copy(c.email, reader)
+	if err != nil {
+		logging.Warnf("failed to copy mail: %v", err)
+	}
+	err = c.email.Sync()
+	if err != nil {
+		logging.Warnf("failed to sync mail: %v", err)
+	}
 }
 
 func (c *Composer) AppendPart(mimetype string, params map[string]string, body io.Reader) error {
@@ -648,7 +679,10 @@ func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
 		if err != nil {
 			return err
 		}
-		io.Copy(writer, &buf)
+		_, err = io.Copy(writer, &buf)
+		if err != nil {
+			return fmt.Errorf("failed to write message: %w", err)
+		}
 		return nil
 
 	} else {
@@ -1242,6 +1276,9 @@ func (c *Composer) checkEncryptionKeys(_ string) bool {
 	// If callbacks were registered, encrypt will be set when user removes
 	// recipients with missing keys
 	c.encrypt = true
-	c.updateCrypto()
+	err = c.updateCrypto()
+	if err != nil {
+		logging.Warnf("failed update crypto: %v", err)
+	}
 	return true
 }
diff --git a/widgets/dirtree.go b/widgets/dirtree.go
index 7b10f02..9486852 100644
--- a/widgets/dirtree.go
+++ b/widgets/dirtree.go
@@ -8,6 +8,7 @@ import (
 
 	"git.sr.ht/~rjarry/aerc/config"
 	"git.sr.ht/~rjarry/aerc/lib/ui"
+	"git.sr.ht/~rjarry/aerc/logging"
 	"git.sr.ht/~rjarry/aerc/worker/types"
 	"github.com/gdamore/tcell/v2"
 )
@@ -363,10 +364,13 @@ func (dt *DirectoryTree) buildTree() {
 
 	dt.list = make([]*types.Thread, 0)
 	for _, node := range threads {
-		node.Walk(func(t *types.Thread, lvl int, err error) error {
+		err := node.Walk(func(t *types.Thread, lvl int, err error) error {
 			dt.list = append(dt.list, t)
 			return nil
 		})
+		if err != nil {
+			logging.Warnf("failed to walk tree: %v", err)
+		}
 	}
 }
 
@@ -420,12 +424,15 @@ func isVisible(node *types.Thread) bool {
 }
 
 func getAnyUid(node *types.Thread) (uid uint32) {
-	node.Walk(func(t *types.Thread, l int, err error) error {
+	err := node.Walk(func(t *types.Thread, l int, err error) error {
 		if t.FirstChild == nil {
 			uid = t.Uid
 		}
 		return nil
 	})
+	if err != nil {
+		logging.Warnf("failed to get uid: %v", err)
+	}
 	return
 }
 
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 1ddf46d..96b91cf 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -13,6 +13,7 @@ import (
 	"git.sr.ht/~rjarry/aerc/lib"
 	"git.sr.ht/~rjarry/aerc/lib/format"
 	"git.sr.ht/~rjarry/aerc/lib/ui"
+	"git.sr.ht/~rjarry/aerc/logging"
 	"git.sr.ht/~rjarry/aerc/models"
 	"git.sr.ht/~rjarry/aerc/worker/types"
 )
@@ -93,7 +94,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
 
 		for i := len(threads) - 1; i >= 0; i-- {
 			var lastSubject string
-			threads[i].Walk(func(t *types.Thread, _ int, currentErr error) error {
+			err := threads[i].Walk(func(t *types.Thread, _ int, currentErr error) error {
 				if currentErr != nil {
 					return currentErr
 				}
@@ -132,6 +133,9 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
 				row++
 				return nil
 			})
+			if err != nil {
+				logging.Warnf("failed to walk threads: %v", err)
+			}
 			if row >= ctx.Height() {
 				break
 			}
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index be578b9..115eb3f 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -645,7 +645,10 @@ func (pv *PartViewer) attemptCopy() {
 			pv.copySourceToSinkStripAnsi()
 		} else {
 			// if it's binary we have to rely on the filter to be sane
-			io.Copy(pv.sink, pv.source)
+			_, err := io.Copy(pv.sink, pv.source)
+			if err != nil {
+				logging.Warnf("failed to copy: %w", err)
+			}
 		}
 		pv.sink.Close()
 	}()
@@ -667,14 +670,23 @@ func (pv *PartViewer) writeMailHeaders() {
 			}
 			field := fmt.Sprintf(
 				"%s: %s\n", fields.Key(), value)
-			pv.pagerin.Write([]byte(field))
+			_, err = pv.pagerin.Write([]byte(field))
+			if err != nil {
+				logging.Errorf("failed to write to stdin of pager: %v", err)
+			}
 		}
 		// virtual header
 		if len(info.Labels) != 0 {
 			labels := fmtHeader(info, "Labels", "")
-			pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
+			_, err := pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
+			if err != nil {
+				logging.Errorf("failed to write to stdin of pager: %v", err)
+			}
+		}
+		_, err := pv.pagerin.Write([]byte{'\n'})
+		if err != nil {
+			logging.Errorf("failed to write to stdin of pager: %v", err)
 		}
-		pv.pagerin.Write([]byte{'\n'})
 	}
 }
 
@@ -689,7 +701,10 @@ func (pv *PartViewer) hyperlinks(r io.Reader) (reader io.Reader) {
 func (pv *PartViewer) copyFilterOutToPager() {
 	stdout, _ := pv.filter.StdoutPipe()
 	stderr, _ := pv.filter.StderrPipe()
-	pv.filter.Start()
+	err := pv.filter.Start()
+	if err != nil {
+		logging.Warnf("failed to start filter: %v", err)
+	}
 	ch := make(chan interface{})
 	go func() {
 		defer logging.PanicHandler()
@@ -718,7 +733,10 @@ func (pv *PartViewer) copyFilterOutToPager() {
 
 		<-ch
 		<-ch
-		pv.filter.Wait()
+		err := pv.filter.Wait()
+		if err != nil {
+			logging.Warnf("failed to wait for the filter process: %v", err)
+		}
 		pv.pagerin.Close()
 	}()
 }
@@ -732,7 +750,10 @@ func (pv *PartViewer) copySourceToSinkStripAnsi() {
 	for scanner.Scan() {
 		text := scanner.Text()
 		text = ansi.ReplaceAllString(text, "")
-		io.WriteString(pv.sink, text+"\n")
+		_, err := io.WriteString(pv.sink, text+"\n")
+		if err != nil {
+			logging.Warnf("failed write ", err)
+		}
 	}
 	if err := scanner.Err(); err != nil {
 		fmt.Fprintf(os.Stderr, "failed to read line: %v\n", err)
@@ -819,7 +840,10 @@ func (pv *PartViewer) Draw(ctx *ui.Context) {
 
 func (pv *PartViewer) Cleanup() {
 	if pv.pager != nil && pv.pager.Process != nil {
-		pv.pager.Process.Kill()
+		err := pv.pager.Process.Kill()
+		if err != nil {
+			logging.Warnf("failed to kill pager process: %v", err)
+		}
 		pv.pager = nil
 	}
 }
diff --git a/widgets/terminal.go b/widgets/terminal.go
index 962b02e..293481d 100644
--- a/widgets/terminal.go
+++ b/widgets/terminal.go
@@ -190,8 +190,14 @@ func (term *Terminal) Close(err error) {
 		term.pty = nil
 	}
 	if term.cmd != nil && term.cmd.Process != nil {
-		term.cmd.Process.Kill()
-		term.cmd.Wait()
+		err := term.cmd.Process.Kill()
+		if err != nil {
+			logging.Warnf("failed to kill process: %v", err)
+		}
+		err = term.cmd.Wait()
+		if err != nil {
+			logging.Warnf("failed for wait for process to terminate: %v", err)
+		}
 		term.cmd = nil
 	}
 	if !term.closed && term.OnClose != nil {
@@ -277,7 +283,10 @@ func (term *Terminal) Draw(ctx *ui.Context) {
 
 		if ctx.Width() != cols || ctx.Height() != rows {
 			term.writeMutex.Lock()
-			pty.Setsize(term.pty, &winsize)
+			err := pty.Setsize(term.pty, &winsize)
+			if err != nil {
+				logging.Warnf("failed to set terminal size: %v", err)
+			}
 			term.vterm.SetSize(ctx.Height(), ctx.Width())
 			term.writeMutex.Unlock()
 			rect := vterm.NewRect(0, ctx.Width(), 0, ctx.Height())
diff --git a/worker/imap/open.go b/worker/imap/open.go
index 15cfa31..c734ba8 100644
--- a/worker/imap/open.go
+++ b/worker/imap/open.go
@@ -130,7 +130,7 @@ func (imapw *IMAPWorker) handleDirectoryThreaded(
 			// Only initialize if we are not filtering
 			var uids []uint32
 			for i := len(aercThreads) - 1; i >= 0; i-- {
-				aercThreads[i].Walk(func(t *types.Thread, level int, currentErr error) error {
+				aercThreads[i].Walk(func(t *types.Thread, level int, currentErr error) error { //nolint:errcheck // error indicates skipped threads
 					uids = append(uids, t.Uid)
 					return nil
 				})
diff --git a/worker/mbox/models.go b/worker/mbox/models.go
index f97530e..d98b14f 100644
--- a/worker/mbox/models.go
+++ b/worker/mbox/models.go
@@ -87,7 +87,10 @@ func (md *mailboxContainer) Copy(dest, src string, uids []uint32) error {
 			if err != nil {
 				return fmt.Errorf("could not get flags for message with uid %d", uidSrc)
 			}
-			destmbox.Append(r, flags)
+			err = destmbox.Append(r, flags)
+			if err != nil {
+				return fmt.Errorf("could not append data to mbox: %v", err)
+			}
 		}
 	}
 	md.mailboxes[dest] = destmbox
diff --git a/worker/notmuch/lib/database.go b/worker/notmuch/lib/database.go
index ac1a28e..780e7ab 100644
--- a/worker/notmuch/lib/database.go
+++ b/worker/notmuch/lib/database.go
@@ -234,7 +234,11 @@ func (db *DB) msgModify(key string,
 		}
 		defer msg.Close()
 
-		cb(msg)
+		err = cb(msg)
+		if err != nil {
+			logging.Warnf("callback failed: %v", err)
+		}
+
 		err = msg.TagsToMaildirFlags()
 		if err != nil {
 			logging.Errorf("could not sync maildir flags: %v", err)
@@ -248,10 +252,16 @@ func (db *DB) MsgModifyTags(key string, add, remove []string) error {
 	err := db.msgModify(key, func(msg *notmuch.Message) error {
 		ierr := msg.Atomic(func(msg *notmuch.Message) {
 			for _, t := range add {
-				msg.AddTag(t)
+				err := msg.AddTag(t)
+				if err != nil {
+					logging.Warnf("failed to add tag: %v", err)
+				}
 			}
 			for _, t := range remove {
-				msg.RemoveTag(t)
+				err := msg.RemoveTag(t)
+				if err != nil {
+					logging.Warnf("failed to add tag: %v", err)
+				}
 			}
 		})
 		return ierr
diff --git a/worker/types/thread.go b/worker/types/thread.go
index 705b3e4..d37d206 100644
--- a/worker/types/thread.go
+++ b/worker/types/thread.go
@@ -101,7 +101,7 @@ func getMaxUID(thread *Thread) uint32 {
 	// TODO: should we make this part of the Thread type to avoid recomputation?
 	var Uid uint32
 
-	thread.Walk(func(t *Thread, _ int, currentErr error) error {
+	_ = thread.Walk(func(t *Thread, _ int, currentErr error) error {
 		if t.Uid > Uid {
 			Uid = t.Uid
 		}