Backend Development 18 min read

Common Go Error Handling Mistakes and Best Practices

This article examines typical Go error handling pitfalls—including misuse of panic, improper error wrapping, incorrect error comparisons, and neglecting error checks—provides illustrative code samples, explains their potential impacts, and offers best‑practice recommendations to write robust, maintainable Go programs.

FunTester
FunTester
FunTester
Common Go Error Handling Mistakes and Best Practices

In Go, error handling differs from traditional object‑oriented languages by using returned error values. While concise, developers often overlook proper error handling, leading to hidden bugs and unstable programs.

Error 48: Panicking (#48)

Example code:

package main

import (
    "fmt"
    "os"
)

func LoadFunTesterConfig() {
    config, err := os.Open("FunTester.conf")
    if err != nil {
        panic(fmt.Sprintf("FunTester: 配置文件加载失败: %v", err))
    }
    defer config.Close()
    // 读取配置文件内容
    fmt.Println("FunTester: 配置文件已加载")
}

func main() {
    LoadFunTesterConfig()
    fmt.Println("FunTester: 程序继续运行")
}

Explanation: panic is meant for unrecoverable errors. Overusing it causes abrupt termination and makes testing difficult.

Possible impact: Using panic for recoverable errors can crash the program and obscure error handling logic.

Best practice: Use panic only for truly unrecoverable situations and prefer returning errors. When panic is used, recover with recover if possible.

Improved code:

package main

import (
    "fmt"
    "os"
)

func LoadFunTesterConfig() error {
    config, err := os.Open("FunTester.conf")
    if err != nil {
        return fmt.Errorf("FunTester: 配置文件加载失败: %w", err)
    }
    defer config.Close()
    fmt.Println("FunTester: 配置文件已加载")
    return nil
}

func main() {
    if err := LoadFunTesterConfig(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println("FunTester: 程序继续运行")
}

Output:

FunTester: 配置文件已加载
FunTester: 程序继续运行

Error 49: Improper Error Wrapping (#49)

Example code:

package main

import (
    "fmt"
)

func ReadFunTesterFile(filename string) error {
    // 模拟读取文件错误
    return fmt.Errorf("FunTester: 无法读取文件 %s", filename)
}

func main() {
    err := ReadFunTesterFile("FunTester.txt")
    if err != nil {
        fmt.Printf("FunTester: 发生错误: %v\n", err)
        // 错误被进一步包装
        err = fmt.Errorf("FunTester: 处理文件时出错: %w", err)
    }
    fmt.Println("FunTester: 程序结束")
}

Explanation: Wrapping errors provides context, but excessive or unnecessary wrapping makes the original error hard to detect.

Possible impact: Over‑wrapping can clutter the error chain and hinder debugging.

Best practice: Wrap errors only when adding meaningful context, using fmt.Errorf with the %w verb.

Improved code:

package main

import (
    "fmt"
)

func ReadFunTesterFile(filename string) error {
    // 模拟读取文件错误
    return fmt.Errorf("不可恢复的错误")
}

func OpenFunTesterFile(filename string) error {
    err := ReadFunTesterFile(filename)
    if err != nil {
        return fmt.Errorf("FunTester: 处理文件 %s 时出错: %w", filename, err)
    }
    return nil
}

func main() {
    err := OpenFunTesterFile("FunTester.txt")
    if err != nil {
        fmt.Printf("FunTester: 发生错误: %v\n", err)
        // 不进一步包装,保持错误链清晰
    }
    fmt.Println("FunTester: 程序结束")
}

Output:

FunTester: 发生错误: FunTester: 处理文件 FunTester.txt 时出错: 不可恢复的错误
FunTester: 程序结束

Error 50: Incorrect Error Type Comparison (#50)

Example code:

package main

import (
    "errors"
    "fmt"
)

var ErrFunTesterNotFound = errors.New("FunTester: 未找到")

func GetFunTester(id int) error {
    if id != 1 {
        return fmt.Errorf("FunTester: 获取FunTester失败: %w", ErrFunTesterNotFound)
    }
    return nil
}

func main() {
    err := GetFunTester(2)
    if err != nil {
        // 错误比较不正确
        if err == ErrFunTesterNotFound {
            fmt.Println("FunTester: FunTester未找到")
        } else {
            fmt.Println("FunTester: 其他错误", err)
        }
    }
}

Explanation: Using == to compare wrapped errors fails; errors.Is or errors.As should be used.

Possible impact: Incorrect comparison may miss specific error handling paths, reducing reliability.

Best practice: Use errors.Is or errors.As for error comparison.

Improved code:

package main

import (
    "errors"
    "fmt"
)

var ErrFunTesterNotFound = errors.New("FunTester: 未找到")

func GetFunTester(id int) error {
    if id != 1 {
        return fmt.Errorf("FunTester: 获取FunTester失败: %w", ErrFunTesterNotFound)
    }
    return nil
}

func main() {
    err := GetFunTester(2)
    if err != nil {
        // 使用 errors.Is 进行正确的错误比较
        if errors.Is(err, ErrFunTesterNotFound) {
            fmt.Println("FunTester: FunTester未找到")
        } else {
            fmt.Println("FunTester: 其他错误", err)
        }
    }
}

Output:

FunTester: 其他错误 FunTester: 获取FunTester失败: FunTester: 未找到

Error 51: Incorrect Error Value Comparison (#51)

Example code:

package main

import (
    "errors"
    "fmt"
)

var ErrFunTesterNotFound = errors.New("FunTester: 未找到")

func GetFunTester(id int) error {
    if id != 1 {
        return fmt.Errorf("FunTester: 获取FunTester失败: %w", ErrFunTesterNotFound)
    }
    return nil
}

func main() {
    err := GetFunTester(2)
    if err != nil {
        // 错误对象值比较不正确
        if err.Error() == "FunTester: 未找到" {
            fmt.Println("FunTester: FunTester未找到")
        } else {
            fmt.Println("FunTester: 其他错误", err)
        }
    }
}

Explanation: Comparing err.Error() strings is fragile and inefficient.

Best practice: Use errors.Is or errors.As for reliable error comparison.

Improved code:

package main

import (
    "errors"
    "fmt"
)

var ErrFunTesterNotFound = errors.New("FunTester: 未找到")

func GetFunTester(id int) error {
    if id != 1 {
        return fmt.Errorf("FunTester: 获取FunTester失败: %w", ErrFunTesterNotFound)
    }
    return nil
}

func main() {
    err := GetFunTester(2)
    if err != nil {
        // 使用 errors.Is 进行正确的错误比较
        if errors.Is(err, ErrFunTesterNotFound) {
            fmt.Println("FunTester: FunTester未找到")
        } else {
            fmt.Println("FunTester: 其他错误", err)
        }
    }
}

Output:

FunTester: 其他错误 FunTester: 获取FunTester失败: FunTester: 未找到

Error 52: Handling the Same Error Twice (#52)

Example code:

package main

import "fmt"

func processFunTester() error {
    return fmt.Errorf("FunTester: 发生错误")
}

func main() {
    err := processFunTester()
    if err != nil {
        fmt.Println("FunTester: 错误:", err)
        // 再次处理同一个错误
        fmt.Println("FunTester: 再次处理错误:", err)
    }
}

Explanation: Processing the same error multiple times leads to redundant logs and confusing error flow.

Best practice: Decide whether a function should handle the error internally or return it for the caller to handle, avoiding duplicate processing.

Improved code:

package main

import "fmt"

func processFunTester() error {
    return fmt.Errorf("FunTester: 发生错误")
}

func main() {
    err := processFunTester()
    if err != nil {
        // 仅由调用方处理错误
        fmt.Println("FunTester: 错误:", err)
        // 调用方决定是否进一步处理
    }
}

Output:

FunTester: 错误: FunTester: 发生错误

Error 53: Ignoring Errors (#53)

Example code:

package main

import (
    "fmt"
    "os"
)

func main() {
    // 忽略读取文件时的错误
    data, _ := os.ReadFile("FunTester.txt")
    fmt.Println("FunTester: 文件内容 =", string(data))
}

Explanation: Ignoring errors, especially in I/O operations, can cause silent failures and unstable behavior.

Best practice: Always check returned errors; at minimum log them for later investigation.

Improved code:

package main

import (
    "fmt"
    "os"
)

func main() {
    data, err := os.ReadFile("FunTester.txt")
    if err != nil {
        fmt.Printf("FunTester: 读取文件失败: %v\n", err)
        return
    }
    fmt.Println("FunTester: 文件内容 =", string(data))
}

Output:

FunTester: 文件内容 = FunTester演示内容

Error 54: Ignoring Errors in defer (#54)

Example code:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("FunTester_output.txt")
    if err != nil {
        fmt.Println("FunTester: 无法创建文件")
        return
    }
    defer file.Close()

    _, err = file.WriteString("FunTester: 写入内容")
    if err != nil {
        fmt.Println("FunTester: 写入时发生错误")
    }
}

Explanation: Errors returned by functions called in defer are often ignored, which can lead to resource leaks.

Best practice: Capture and handle errors inside the deferred function, e.g., using an anonymous closure.

Improved code:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("FunTester_output.txt")
    if err != nil {
        fmt.Println("FunTester: 无法创建文件")
        return
    }
    defer func() {
        if cerr := file.Close(); cerr != nil {
            fmt.Printf("FunTester: 关闭文件时出错: %v\n", cerr)
        }
    }()

    _, err = file.WriteString("FunTester: 写入内容")
    if err != nil {
        fmt.Printf("FunTester: 写入时发生错误: %v\n", err)
    }
}

Output file FunTester_output.txt content:

FunTester: 写入内容

By handling errors properly—especially in deferred cleanup—programs become more robust and easier to debug.

backendprogramminggobest practiceserror handling
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.