TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Gopher Wrangling: Effective error handling in Go

129 pointsby stephen123almost 2 years ago

26 comments

janosdalmost 2 years ago
Go&#x27;s error handling is a horrible mess:<p>1. It&#x27;s easy to ignore returned errors without any compiler warnings. You have to rely on third party tools such as golangci-lint to report missing error handling.<p>2. Errors don&#x27;t carry stack traces with them, you have to rely on third party libraries or custom errors to get that functionality and you will only get it for your own code, not in other libraries you are using.<p>3. It&#x27;s unclear who should add context to error messages is it the caller or callee? Usually it gets skipped, leading to useless error messages.<p>4. Errors are untyped. If you want to decide based on error types, you have to use errors.Is or errors.As, which, surprise, is roughly as expensive computationally as panic-recover. (Source: I did a performance tests on this with Go 1.18) Go might as well add a simpler way to create exceptions. (I wrote a prototype library to that effect a while ago: <a href="https:&#x2F;&#x2F;github.com&#x2F;APItalist&#x2F;lang">https:&#x2F;&#x2F;github.com&#x2F;APItalist&#x2F;lang</a> )<p>5. Error messages are too terse and hard to read when using the recommended semantic of &quot;message (cause(cause(cause)))&quot;. I&#x27;d rather see stack traces, that&#x27;s much more useful.<p>6. Most loggers are globally scoped and cannot be injected into code, leading to an all-or-nothing approach. It is not uncommon that you have 3-4 logging libraries as dependencies, which you need to configure separately (if you even can). Also, good luck securing this mess.
评论 #36400234 未加载
评论 #36400899 未加载
评论 #36403130 未加载
评论 #36401952 未加载
评论 #36401063 未加载
评论 #36399989 未加载
eddythompson80almost 2 years ago
There is a lot that I like about Go. Error handling is not one of them. On one hand, I appreciate the simplicity of it all. Nothing special about an error, it’s just part of how you do everything else. But on the other hand, there is something clearly special about an error. It’s something 100% of go users have to deal with in almost every single function call. There is something clearly special about it. These grassroots patterns and efforts to handle multiple errors per function, or async errors etc all should really have better solutions in the language.<p>I understand that maybe the language authors in the early days didn’t want to lock anyone into a strict paradigm for how to deal with errors. Like I’m not thrilled about Java’s approach either, but that can never change. But Go is a very popular and established language now. It’s time to fix the error handling mess. There are so many good examples out there to get inspiration from. F#, Swift and Rust have a perfect error handling mechanism.
评论 #36398060 未加载
评论 #36398059 未加载
评论 #36398528 未加载
评论 #36398663 未加载
kaba0almost 2 years ago
Could someone explain why is Go so hyped?<p>In my personal opinion it is just not a good language, and I think many judge it based on some false basis that it is somehow “close to the hardware” because it produces a binary. Like, the amount of time it is put next to Rust when the two have almost nothing in common..<p>It is very verbose, yet Java is the one that is called that, often by Gophers, which is much more concise. It has terrible expressivity, a managed language which is a perfectly fine design choice, yet seemingly every other language with a GC is somehow living in sin.<p>And still, it doesn’t fail to show up each day on HN.
评论 #36399727 未加载
评论 #36400229 未加载
评论 #36399758 未加载
评论 #36400143 未加载
评论 #36400235 未加载
评论 #36400314 未加载
评论 #36400399 未加载
评论 #36399738 未加载
评论 #36400062 未加载
评论 #36400424 未加载
评论 #36400928 未加载
评论 #36400481 未加载
skarlsoalmost 2 years ago
This feels like it has been written by someone who recently started using the language, considering that the code in many places simply doesn&#x27;t compile and has syntax errors or logical errors in it.<p>Many people coming into Go as a new language immediately start bickering about how they want their previous language features in Go rather than accept what Go has to offer and at least try to understand it. This is the equivalent of moving to another country and then refusing to integrate but being very vocal about how said country sucks.<p>I genuinely appreciate Go&#x27;s error handling because it&#x27;s clean and on the nose. It&#x27;s not hidden behind weird syntax&#x2F;values that you have to unpack. It&#x27;s right in your face all the time. When you read the code, it reads cleanly and understandably, even for a beginner. They don&#x27;t have to adapt to some weird combination of failures &#x2F; unpacking&#x2F;choosing something different when there is an error; you immediately see that there could be an error.<p>And regarding stack traces, wrapping errors will provide you with failure locations to the line code. You can have all sorts of nice output for errors you can later parse and identify.<p>I get that some people go into Go because of a shift in the company and have no choice; I feel you. For me, it was a life changer. I learned to love coding again after 15 years of writing Java Beans, Spring annotations, CreateMyFriggingObjectFactorySingletonBuilderFactoryBuilders.
评论 #36402792 未加载
bedobialmost 2 years ago
For the love of all that is good in the world, this is a solved problem, I don&#x27;t understand why languages like Go, Kotlin, Python etc etc etc continue to insist on not having sane Option, Either, Try etc types.
评论 #36399585 未加载
评论 #36399031 未加载
评论 #36399610 未加载
评论 #36399548 未加载
evercastalmost 2 years ago
&gt; Usually this isn’t necessary and its better to just return the error unwrapped.<p>This is a terrible advice. Wrapping is extremely helpful in providing additional context for the error travelling up the call stack. Without wrapping, one typically ends up with software logging generic errors like &quot;file not found&quot; , which you can&#x27;t act on because... you don&#x27;t know where it&#x27;s coming from. If you skip error wrapping, better be ready to enjoy quality time when production crashes.
za3faranalmost 2 years ago
The provided examples highlight exactly why error handling in golang is verbose, error prone, and lacks context. Do people really not care about stack traces?
评论 #36399466 未加载
评论 #36400772 未加载
评论 #36400247 未加载
tester457almost 2 years ago
Go error &quot;handling&quot; blocks don&#x27;t seem like error handling, when it&#x27;s 3+ visual polluting LOC that just return the error up the call stack, occasionally with context like tip #4 of the blogpost.<p>I&#x27;ve tried to like go&#x27;s verbose error handling (follow the “happy path”) but the error handling signal to noise ratio is skewed in a way that makes developing in go feel slow and boring.
评论 #36398612 未加载
dvtalmost 2 years ago
For a language where coroutines are such a first-class citizen, I wish there was a more idiomatic way of returning and handling async errors in Go. I know it&#x27;s all over the docs, but using a channel has always felt so &quot;wrong.&quot; The errgroup lib tries to fix this, but it&#x27;s still not as flexible as using a channel (for example, if you want to store or log <i>all</i> routine errors).
评论 #36399803 未加载
marcus_holmesalmost 2 years ago
Surprised that the &quot;always wrap your errors&quot; rule isn&#x27;t in there. It&#x27;s been the rule in the last few Go teams I&#x27;ve been in.
评论 #36398670 未加载
评论 #36399671 未加载
Jacobinskialmost 2 years ago
In the last example, it is preferable to use `if errors.Is(err, context.DeadlineExceeded) {...}` instead of the given `if err == context.DeadlineExceeded {...}` since the `errors.Is()` function will recursively unwrap error chains to find the specified error.<p><a href="https:&#x2F;&#x2F;go.dev&#x2F;blog&#x2F;go1.13-errors" rel="nofollow noreferrer">https:&#x2F;&#x2F;go.dev&#x2F;blog&#x2F;go1.13-errors</a>
showdedddalmost 2 years ago
For #3 you can also use errgroup from sync&#x2F;errgroup. It&#x27;s a nice recent addition to the stdlib.<p>For #4 wrapping your errors creates pretty and logical error messages for free. It should be done in most cases.
评论 #36398501 未加载
movedxalmost 2 years ago
One thing I did for a project of mine was define my own error type, so that I can include some specific information for the next layer up. In short, I added information about the severity of the error so that the calling function that&#x27;s capturing it can decide if it can recover from it or not based on the severity, and I added a flag to determine if the result value (think &quot;T, error&quot;) is empty, partial, or complete.<p>I added .Empty() and .Partial() because if you&#x27;re returning &quot;string, error&quot; from a function, for example, then &quot;&quot; doesn&#x27;t cut it for me and instead of checking for &quot;&quot; in the calling function, I can instead check for err.Empty(). This doesn&#x27;t seem like it&#x27;s useful, but take that idea and apply it to two additional scenarios: a non-pointer to a struct{} with 10 fields (are they <i>all</i> empty?), and partial return values i.e. the function you called threw a warning and only partially populated the return value. Now the calling function can shift the &quot;is empty&quot; checks to the function that actually constructs the return value (or not.)<p>Now I can call a function, get my custom error type back, and I can determine if there was an issue and whether or not the value is empty or partial regardless of the type (and its complexity.) This paid me back in dividends the moment I wanted to be able to return a warning <i>and</i> a partial result - so not workflow breaking, but also not everything the caller asked for... it&#x27;s up to the caller to determine if it has what it needs to continue.
talideonalmost 2 years ago
There was just so much nonsense back in the day around Go&#x27;s error handling and about how it was so much more straightforward than adding exceptions to the language.<p>In reality, the only reason why errors in Go work the way they do is that it kept the runtime simpler by offloading checking to the developer. The alternative would&#x27;ve been for Go to support sum types, which would&#x27;ve helped make error handling a lot saner, but that was dismissed because they overlapped a little with structurally-typed interfaces (Go&#x27;s one really good idea). Oh, and the stupid hack that is &#x27;iota&#x27;.<p>And then Go eventually ended up badly re-inventing most of what exceptions do with errors.Is(), errors.As(), and fmt.Errorf(&quot;%w&quot;, err).<p>It&#x27;s such a hot mess.
评论 #36402952 未加载
quicklimealmost 2 years ago
&gt; Make it the top layer’s responsibility and don’t log in any services or lower level code.<p>&gt; Make sure your logging framework is including stack traces so you can trace the error to its cause.<p>&gt; For example in a web app you would log the error in the http handler when returning the Internal Server status code.<p>This is different from how I do it, am I doing anything wrong?<p>I prefer to make it the <i>bottom</i> layer’s responsibility - so, the first source of the error at the boundary of my application and the library that produces the error, rather than the top level of the http handler.<p>Go errors infamously don’t include stack traces, so how are you supposed to know where your error originated from if you log it from the top level of the http handler?
softirqalmost 2 years ago
Always wrapping errors can be a good way to get a stack trace of the error path in the logs.
评论 #36398004 未加载
euroderfalmost 2 years ago
I like that errors stay in the flow of control. No &quot;Exception&quot;s leaping up thru multiple levels of call stack. Instead the plan is: 1) see the error, 2) &quot;%w&quot; the error, 3) kick it upstairs, 4) Mission Accomplished. And at some level, some piece of code will grab the bull by the horns and wrestle it to the ground.<p>All in all, errors-as-values is a calm way to deal with unhappy code paths. A clear renunciation of longjump.<p>(Golang system-originated panics are excepted from this gloss, but they are defined quite narrowly, and ofc catchable.)
dpifkealmost 2 years ago
I&#x27;ve mostly evolved to making err a named return parameter, and inverting the err != nil check. For example:<p><pre><code> func foo() (err error) { var x any if x, err = bar(); err == nil { err = baz(x) } if err == nil { err = bat() } if err != nil { err = fmt.Errorf(&quot;%w doing foo &lt;additional info here&gt;&quot;, err) } return } </code></pre> This feels somewhat cleaner to me, in particular by combining error handling (in this case just a simple wrap) in a single place at the end of the function.
评论 #36398587 未加载
评论 #36398569 未加载
评论 #36399154 未加载
flippinburgersalmost 2 years ago
Man I definitely handle errors the &quot;wrong way&quot; in almost all go code I have written. I take a &quot;log immediately with filename and line number&quot; approach. For me, it works. For teams maybe not. For large codebases with a bunch of &#x27;I am a &quot;programmer&quot; (because it makes me loads of cash) individuals&#x27;, it is definitely not a good idea. It requires discipline. Personally I hate stack traces.
suralindalmost 2 years ago
Do not wrap your errors like in the article. A better way to do it is to create your own error type where you can pass additional values alongside message. It means that you can actually handle the error and not just log it.
drakonkaalmost 2 years ago
I know there&#x27;s lots of complaint about error handling in Go, but I always liked it. I find it straightforward, intuitive, and it forces you to be absolutely explicit if you _really_ want to ignore something.
评论 #36405771 未加载
nathantsalmost 2 years ago
gopls, staticcheck, errcheck and ineffassign are non-optional for golang dev.<p>add them to flycheck or similar, and go is a fantastic experience.<p>should they be part of the compiler? maybe. i’m not losing sleep over it.
incompatiblealmost 2 years ago
&quot;Always handle errors&quot; sounds good until you remember that every read or write can potentially fail. My go programs are littered with unchecked fmt.Printf or Println statements.
评论 #36398157 未加载
评论 #36398618 未加载
saturn_vkalmost 2 years ago
1. It&#x27;s so easy to skip errors that I have yet to encounter such a problem across two companies now. It&#x27;s weird
评论 #36403261 未加载
liampullesalmost 2 years ago
I differ with the author here, I prefer to log errors as soon as they come into &quot;my&quot; code (e.g. from external library or network call, etc.).<p>This is a good rule for any language, because you always ensure an error is logged once. In Go, you can add additional info from the caller to the Context to log higher level info, e.g. a trace span Id.
评论 #36403159 未加载
hknmttalmost 2 years ago
Majority of people who complain about errors in Go don&#x27;t primarily work with compiled languages that produce programs that run indefinitely or for a very long periods of time. It&#x27;s easy to throw exception in PHP which is interpreted on the fly and run once so any failure can be simply thrown out and ignored. With constantly running programs one has to always handle all the cases where things don&#x27;t go as wanted to prevent program form crashing. If people truly hate Go&#x27;s errors, just panic, it&#x27;s literally no different than exceptions in other languages. You can catch them and stop or continue whatever code you want. Just STFU about errors in Go already!