Fixing Unhandled Errors in Go: Stop Ignoring Your Return Values

beginner🔷 Go2026-05-12| Go (Golang) projects using linters like golangci-lint, errcheck, or revive.

Error Message

unhandled error (Linter warning: Error return value of function Call() is not checked)
#go#error handling#linter#golangci-lint#errcheck

Where things go wrongGo’s design forces you to confront errors head-on. Unlike Java or Python, which rely on try/catch blocks, Go functions return an error as a standard value. Beginners often skip checking these, assuming everything will work as expected. This oversight is precisely what triggers linter warnings.

The standard Go compiler won't block you from ignoring a return value unless you assign it to an unused variable. However, static analysis tools like golangci-lint will catch this instantly. You'll likely see this fail in your CI pipeline or your IDE's terminal.

// This code triggers the warning
func saveConfig(data string) {
    os.WriteFile("config.yaml", []byte(data), 0644) // Error is ignored!
}

Running a check results in this familiar complaint:

main.go:10:14: Error return value of function os.WriteFile is not checked (errcheck)

Analysis: The Risk of Silent FailuresIgnoring an error is a dangerous gamble. If os.WriteFile fails—perhaps due to a full disk or a permission error—your program will keep running as if the 10MB config file was saved successfully. These "silent failures" are a nightmare to debug because the crash usually happens much later in an unrelated part of the stack.

Watch out for these high-risk functions where errors often slip through:

  • Close(): Closing file descriptors or database connections.- Write(): Sending HTTP responses or writing to disk.- json.Unmarshal(): Converting bytes into structs.- Exec(): Running database migrations or updates.## The Quick Fix: The Blank IdentifierNeed the linter to stop shouting while you're prototyping? Use the blank identifier _. This tells Go: "I know this could fail, but I’m choosing to look the other way for now."
func prototype(data string) {
    _ = os.WriteFile("test.txt", []byte(data), 0644)
}

Pro tip: Use this sparingly. Suppressing a linter warning doesn't fix the underlying risk; it just hides it from your tools.

Permanent Fixes: Professional Error Patterns### 1. The Standard Guard ClauseThis is the bread and butter of Go. Check the error immediately and handle it. Don't wait.

func saveConfig(data string) error {
    err := os.WriteFile("config.yaml", []byte(data), 0644)
    if err != nil {
        return fmt.Errorf("could not save config: %w", err)
    }
    return nil
}

2. Capturing Errors in DeferClosing a file is a classic trap. A simple defer f.Close() ignores potential errors during the final flush. If your function returns an error, use a named return variable to capture failures during cleanup.

func processFile(path string) (err error) {
    f, err := os.Open(path)
    if err != nil {
        return err
    }

    defer func() {
        closeErr := f.Close()
        // Only overwrite the main error if it's currently nil
        if err == nil {
            err = closeErr
        }
    }()

    // Process file logic here
    return nil
}

3. The "Log and Continue" ApproachSometimes you can't bubble an error up—like inside a background goroutine. In these cases, visibility is key. Log the error so your Sentry or Datadog dashboard can track it.

defer func() {
    if err := r.Body.Close(); err != nil {
        log.Printf("CRITICAL: Failed to close body: %v", err)
    }
}()

4. When it's actually safe to ignoreYou’ll rarely see anyone check the error returned by fmt.Println. If writing to stdout fails, your environment is likely already crashing. To keep linters happy in these rare scenarios, be explicit.

_, _ = fmt.Fprintln(os.Stderr, "Emergency log entry")

Verification: Confirming the Clean SlateOnce you've updated your patterns, verify the fix by running the industry-standard suite. If you don't have golangci-lint, it’s worth the 2-minute setup.

# Run all enabled linters
golangci-lint run ./...

For a focused check on just unhandled errors, use errcheck:

# Install if you haven't yet
go install github.com/kisielk/errcheck@latest

# Audit your project
errcheck ./...

Zero output is your goal. It means your code is robust and your team won't be chasing phantom bugs in production.

Related Error Notes