The Ongoing Debate Over Go’s Error Handling Controversy
Note: The core content was generated by an LLM, with human fact-checking and structural refinement.
Go’s approach to error handling has been a consistent topic of discussion and complaint within the programming community, often described as verbose and repetitive. Despite its pervasiveness, the Go team has officially announced a halt to pursuing further syntactic changes for error handling for the foreseeable future, citing a lack of consensus on any proposed solutions.
Go Team Stance
The Go team acknowledges that the verbosity of error handling is one of the “oldest and most persistent complaints” about the language. They state that they take community feedback seriously and have, for many years, attempted to address this problem.
However, after numerous attempts, including three full-fledged proposals from the Go team itself and “literally hundreds” of community proposals, none have garnered “sufficient (let alone overwhelming) support”. As a result, the Go team has decided not to proceed with syntactic changes for error handling, explaining that the goal of their proposal process is to reach general consensus in a timely manner, which has not been achieved here. They further note that even the most senior members of the Go team at Google “do not unanimously agree on the best path forward”. For the foreseeable future, the team will “stop pursuing syntactic language changes for error handling” and will close related open and incoming proposals.
Criticisms of Go’s Error Handling
The primary criticism revolves around the verbosity and repetitiveness of the if err != nil
pattern, which critics argue can “drown out the rest of the code” and make functions appear “50% error handling where the error handling actually DOES NOTHING”. Many find this “unenjoyable braindead aspect” to be the most time-consuming part of programming in Go.
Beyond verbosity, other criticisms include:
- Errors can be dropped silently or accidentally ignored, which can lead to crashes. As one user points out, while
foo, _ := doSomething()
allows ignoring errors, this can lead to unstable software. - Function call results cannot be stored or passed around easily because they are not treated as singular values like a
Result
type. - The necessity of
errors.Is
and the interaction of “nested” errors with Go’s type system is seen as problematic. - Switching on errors is difficult. Although Go intends for developers to define types for errors to enable casting or switching, some observe that most Go codebases use
errors.New
andfmt.Errorf
, which do not lend themselves to branching on error conditions. - The use of sentinel values in the standard library is criticized.
- Poor interactions with generics often necessitate packages like
errgroup
. - The practice of returning “bogus values” alongside errors is a point of contention, even if those values are considered invalid when an error is present.
- Some believe Go’s error mechanism is based on the assumption that errors are primarily meant to be logged, requiring developers to “get out of [their] way to develop errors that can actually be handled”.
One user described Go’s error handling as “THE worst part of Go. All of it”. Others lament that “Go has unsolved the problem” of error boundaries.
Arguments Against Change
Despite persistent complaints, many Go veterans and the Go team itself present strong arguments for maintaining the status quo:
- Intentional Design and Tradeoffs: Go’s current error handling is an “intentional design that is aware of its tradeoffs” and is considered “the least terrible option in satisfying the language design ethos and developers writing Go”.
- Go’s Success: Go is “massively successful” and “most active Go programmers don’t mind the error handling situation”. Complaints often come from those who don’t frequently use the language.
- Preference for Explicit Control Flow: Many Go users, including a 10-year Go veteran, “strongly prefer Go’s approach to most other languages,” arguing that “implicit control flow is a nightmare that is best avoided”. This explicit nature “forces everyone to think about errors more deeply than in other languages” and ensures the error case receives due attention.
- Readability and Clarity: Many appreciate the “explicit, clear, easy to read ‘verbose’ error handling”. They argue that once familiar with Go, the verbosity “fades into the background”.
- Debugging Benefits: Explicit
if err != nil
statements make it easier to addprintln
statements or set breakpoints for debugging, as the error handling logic is not hidden behind new syntax. - Cost of Language Changes: Introducing new language features is “very expensive,” involving changing existing code, updating documentation, and adjusting tools. The Go team is relatively small, and there are “a lot of other priorities”.
- User Adaptation: Many long-term Go users report that initial resistance to the error handling style “usually fades as they get more familiar with Go’s philosophy and design choices”.
- Lack of Consensus: A significant argument against change is the sheer inability to find community consensus on any single proposal, even among the Go architects themselves. Some believe that “doing nothing is the right thing to do”, especially since a highly popular Go proposal was “leave ‘if err != nil’ alone”.
- “Design by Committee” Concerns: Some critique the Go team’s consensus-driven approach, suggesting it leads to inaction on “user-visible changes”. However, others defend it, noting the Go team is “very picky as committees go” and avoids creating a “design-by-committee junk drawer language”.
Proposed Solutions/Alternatives (Rejected by Go Team)
Over the years, numerous proposals have been put forth to address Go’s error handling verbosity, yet none have been adopted. The Go team has explored various syntactic solutions:
check
andhandle
mechanism (2018): This was the first explicit attempt by the Go team, based on a draft design that included a detailed analysis of alternatives.try
mechanism: This proposal allowed for a more concise way to propagate errors, as shown in this conceptual code snippet:1
2
3
4
5
6func printSum(a, b string) error {
x := try(strconv.Atoi(a)) // If strconv.Atoi(a) returns an error, this try() would return from printSum
y := try(strconv.Atoi(b))
fmt.Println("result:", x + y)
return nil
}However, this was deemed “unpalatable to many” because it affected control flow by returning from potentially deeply nested expressions, thereby “hiding this control flow from view”.
?
operator (2024): Inspired by Rust’s?
operator, this proposal aimed to reduce boilerplate. A conceptual example:1
2
3
4
5
6func printSum(a, b string) error {
x := strconv.Atoi(a) ? // If strconv.Atoi(a) returns an error, this ? would return from printSum
y := strconv.Atoi(b) ?
fmt.Println("result:", x + y)
return nil
}While small informal user studies showed most participants correctly guessed its meaning, this proposal also “quickly overrun with comments and many suggestions for minor tweaks” and ultimately failed to gain broad support.
Community-driven proposals also included:
Sum types like
Result<Value, Failure>
: Advocated as a cleaner way to represent success or failure, similar to Rust or Swift. This would provide “better-typed and enumerated error types”.with
clause: A suggested construct for implicitly handling errors within a block.or
keyword ideas: Syntactic sugar, often coupled with error handling logic, for example:1
n := strconv.Atoi(s) or |err| { return fmt.Errorf("foo: %w", err) }
The Go team states that it has “thoroughly explored the design space for seven years” but could not find community consensus.
Go Design Philosophy
Go’s error handling strongly reflects its core design philosophy:
- Minimal Language Surface Area: Go aims to be a small language. Adding new syntactic constructs for error handling goes against this principle if it introduces multiple ways of doing the same thing.
- Explicit Control Flow: Go’s design prioritizes explicit control flow, meaning what you read is what you get. The
try
mechanism was rejected partly because it “affected control flow by returning from potentially deeply nested expressions, thus hiding this control flow from view”. This explicitness is seen as “forcing everyone to think about errors more deeply”. - Pragmatism: Go is often described as pragmatic. The current error handling, while not elegant to some, “works” and is considered “very much in keeping with the pragmatic spirit of Go”.
- Zero-Value Language: Go is a “zero value language,” meaning “values always contain useful state.” This fundamentally clashes with monadic-type solutions like
Result
types, which would require “designing an entirely new language with a completely different notion about how state should be represented”. - Resistance to Change: The language has a strong “resistance to change and consistent across it’s 15 years”. Changes are considered very carefully and often rejected without overwhelming consensus.
Related Language Design Topics
The discussion around Go’s error handling often intersects with broader language design debates:
- Generics: Before error handling, the lack of generics was Go’s top complaint. Generics took 13 years from Go’s open-source release to be implemented. Unlike error handling, “nobody is forced to use generics”. Some criticize Go’s initial “blunder” of static typing without parametric polymorphism, suggesting it lies at the root of the error handling problem.
- Exceptions (
panic
/recover
): Go does have “Java-style exception handlers” viapanic
andrecover
, which can be used to push values up the stack. However, these are generally considered “orthogonal” to typical error handling and are intended for unrecoverable errors. The debate over new error handling syntax sometimes involves whether it would implicitly “reinvent exceptions”. nil
Safety / Strict Null Checks: Some Go users argue that the lack of strict null checks is a “way bigger problem in practice than error handling,” leading to crashes, and advocate for compiler support to eliminatenil
dereference panics.- Concurrency: Go’s designers prioritized concurrency (CSP-style concurrency with goroutines) over error handling, leading to a “simpler error handling model that doesn’t interfere with goroutines as much”.
- Comparison to Other Languages: Discussions frequently reference error handling approaches in other languages like Rust (
Result
and?
operator), C++ (std::expected
), Java (checked exceptions, which are “hated”), Swift, JavaScript, Kotlin, and Python.
References
[ On | No ] syntactic support for error handling - The Go Programming Language
(On | No) Syntactic Support for Error Handling | Hacker News
More
Recent Articles:
- How to Make Your Go Code Truly Discoverable on Medium on Website
- GOMAXPROCS in Containers: Addressing CPU Limits In Go 1.25 on Medium on Website
Random Article:
More Series Articles about You Should Know In Golang:
https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a
And I’m Wesley, delighted to share knowledge from the world of programming.
Don’t forget to follow me for more informative content, or feel free to share this with others who may also find it beneficial. It would be a great help to me.
Give me some free applauds, highlights, or replies, and I’ll pay attention to those reactions, which will determine whether I continue to post this type of article.
See you in the next article. 👋
中文文章: https://programmerscareer.com/zh-cn/debate-error-handling/
Author: Medium,LinkedIn,Twitter
Note: Originally written at https://programmerscareer.com/debate-error-handling/ at 2025-06-05 01:35.
Copyright: BY-NC-ND 3.0
Comments