Hacker News new | past | comments | ask | show | jobs | submit login

My main issue with errors in go (last time I used it, which was a while ago) is not any of the things mentioned, it's that the default pattern you learn provides no stack trace whatsoever in the console.

There's no error handling in the python code, yet the stack trace will lead you to exactly to where you need to look. To me that is strictly superior when it comes time to debug an error.

Edit: maybe not strictly superior, as has been mentioned in replies, wrapped errors with good context at each step does seem like it would be very high SNR.




This, and most of the time if you’re simply bubbling up an error it’s busy work. Languages like python get out of your way here. If python was able to reliably tell you possible exceptions (within reason - at a certain level generic errors like out of memory can be ignored since they’re globally present) and provide a convenient way to handle them that’d be great.

This is domain dependent, but most of the time errors are handled by “catch it, log it, display an oops and move on”, and optimizing for that case is very pragmatic.


> If python was able to reliably tell you possible exceptions

I know typed errors in Java are regarded by many as a failure... but it is still conceptually very appealing to me.

I love result types and pattern matching in more typed/functional languages, except that they often have similar issues to go re: not having a stack trace.


I liked checked exceptions, but higher-order functions break them. The method signature of stream.map(f) allows a generic type for f but it can't vary with f's exceptions. The choices usually boil down to "only throw unchecked exceptions" (like Runnable) or "throw any exception" (like Callable).


It’s worth noting that this is specifically a shortcoming of Java’s type system and not an unsolvable problem. I’m a fan of Swift’s solution where you can directly mark a function as only rethrowing errors from a given parameter; it’s a lot simpler than making the error type a full fledged type parameter and covers most scenarios.


Stack traces and error handling are different thing. You want a stack when you have an unhandled error and need to figure out what went wrong. By definition "handling" an error goes down some known path where you know where you were and why. I mean, it's true you can handle errors well or badly; but as a general rule if you need a stack to figure out what your handling output means, you did it badly.


It is quite a hard thing to predict which errors will be handled and which will not at the point that you create an error :)

So I disagree. This is a pedantically narrow view of what "error handling" is. Printing a stack trace and crashing is also "handling" it.


> It is quite a hard thing to predict which errors will be handled and which will not at the point that you create an error :)

Not really, no? To first approximation all errors are system call failures: file not found, OS couldn't hand you enough memory so you got a heap allocation failure, network timeout, etc... You know exactly where those can happen because they are exposed as functions with some kind of error code specified as part of the return value.

There are situations where that's not true, but those situations tend to be fairly rare: things like "schema" violations in some backend data store ("product has a null manufacturer field", which should never happen but it turns out it does for data imported via that merger 2 years ago). And these tend to have correspondingly complicated handling ("add it to the queue of manual fixup requests") not well captured by "print a stack trace and crash".


> Not really, no? To first approximation all errors are system call failures

How does this help predict whether they are handled or not higher up the stack?


Again, demanding the need to know what is happening "higher up the stack" to understand an error is a symptom that the error is UNhandled. Handling it means understanding those things, generally by inspecting the return code in the higher caller where the context is understood.

And you're absolutely right, if you don't handle errors than a stack trace is a great way to reverse engineer the context. But that's not "handling" the error, it's debugging. And those are different tasks.


So to be clear, are you saying we don't need stack traces because we should handle 100% of errors instead?


I genuinely don't understand how you get from the sentence "You want a stack when you have an unhandled error and need to figure out what went wrong." to "You are saying we don't need stack traces". All I can suggest is that you re-read the thread more carefully.


It's generally good practice to not just `return err`, but to `return fmt.Errorf("some context: %w", err)`. That's usually enough for you to get what you need, with a much higher signal-to-noise ratio than a stack trace.


Correct me if I'm wrong but that still won't have file names and line numbers, right? So not only do you have to remember to wrap correctly, you need to take care to keep "some context: " a decently greppable string.

I agree that wrapping errors that way is better than not, but I still find it a bit lacking.


A (sad?) workaround I've seen is to intentionally misspell words in error / log messages so that they're unique enough. I guess I don't blame people doing that, but it does point to limitations of the language.


I've started to add types to log messages so they can be easily sorted with a nice GUI. Your way works too.


This, and sooo many other shortcomings, begins to explain why a submission extolling Go's error handling is already flagged.

Go, and it's error handling (and for that matter it ludicrously verbose failure to bubble up errors) is a steaming pile of shit.


That's correct. The resulting error message won't be a stack trace, but I find it spells out the problem quite nicely in practice. I haven't had issues with grepping for the errors, but I can see that it would be a problem in larger codebases.


Easy way around this, when you create the error message always put in there the local epoch time of when you are writing it (copy from date, plug-in in your editor, whatever) making grepping for it trivial later on.


That still does not let you know how you ended up at the error site. There could be multiple paths, and this approach does not help you determine that. Stack traces are still more useful.


i print the error along with file and line number every time i return it. clunky, but it works.

in fact i print file and line with every log message.

https://github.com/nathants/libaws/blob/87fb45b4cae20abd1bb1...


It's not a good practice to print stack trace anyway, you have a network timeout, you don't need 20 lines of stacktraces.

Most logger in Go can it though.


How do you identify which network call is the one that timed out?


You wrap errors with fmt.Errorf(). I work on Go daily and I don't need a stacktrace to tell me where the problem is, the error is self explanatory.


Your errors have messages, most of the time your "did not connect" error gets caught inside your "DiscountService" And returns a "could not connect to discount service error" ...

Its funny that were having this conversation because "discount service" is a thing in my life. Its sporadic failures means I just re-try immediately and get a success... Errors are part of flow control.


> It's not a good practice to print stack trace anyway

What makes it not a good practice?

> you don't need 20 lines of stacktraces

I don't _need_ stacktraces, but they are useful to me.


why would you need a stack trace at a panic? You know where the panic is already. 9/10 Go tutorials have you either panic, or write a message to the console. So write a more meaningful message so you know where in the code it's coming from. Stack traces requires instrumentation which can be stripped. I'd prefer not to ship my debug symbols.


All of that sounds like more work then what you get for 0 effort with a stack trace.


runtime/debug PrintStack() is 0 effort… what are you going on about? Stack traces are not required, only preferred by some because they don’t program defensively. If you want to include debug information in your binaries, go for it - I’ll be waiting with Ghidra


In other languages I don't have to explicitly print the stack. So I would not call that 0 effort.

I do not care about hiding my debug symbols. Anything I write that has a binary you can touch, you can just go read the source directly.


Not everything is open source... I get where you are coming from but there's a lot of software out there, that is written in go, that isn't and they want to keep it that way as much as possible.

   go run main.go
Using the go debugger in your IDE will blow up when that part of the code that panics on error is hit. Do you still need a stack trace then? You're literally looking at it in your editor. There's a time and a place for stack traces in my view and I enjoy the fact that in Go, it's not on by default.

If you're building a cloud service in Go and you're unsure of your branching paths, by all means, use stack traces and eliminate your own errors.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: