Goroutine panic recovery

235 views
Skip to first unread message

Max Claus

unread,
Dec 8, 2025, 2:26:26 PM (2 days ago) Dec 8
to golang-nuts
I recently discovered that I had a misconception about how panic recovery works, especially in HTTP handlers. I wrote an article explaining that misunderstanding and suggested using more recover calls for panics in goroutines started from HTTP handler requests (link to article). That seemed like a reasonable approach based on the http package documentation:

> If ServeHTTP panics, the server (the caller of ServeHTTP) assumes that the effect of the panic was isolated to the active request. It recovers the panic, logs a stack trace to the server error log, and either closes the network connection or sends an HTTP/2 RST_STREAM, depending on the HTTP protocol. (reference)


Reading that, I thought it would be a natural pattern to follow the same logic for goroutines started from HTTP requests. However, the feedback I received on Reddit from other engineers suggested that this is considered a bad practice, and that the built-in recovery mechanism in the HTTP server was a historical mistake that the Go team supposedly regrets. (link to reddit thread)

I’d like to understand this better. Is it actually considered bad practice? And does the Go team really regret the built-in panic recovery in HTTP handlers? Aside from the Google Go style guide and various opinions from engineers online, I haven’t been able to find any official Go document or article that clearly states this. (link to Google style guide, link to someone commenting about it too).

Ian Lance Taylor

unread,
Dec 8, 2025, 2:44:32 PM (2 days ago) Dec 8
to Max Claus, golang-nuts
Yes, in general the Go team considers the fact that the net/http
server recovers panic to be a historical mistake.

Go code in practice does not attempt to be safe in the presence of
panics in code that it calls. This means that in practice a panic can
leave data structures and locks in an inconsistent state. If the panic
is recovered, the future behavior of the program is unpredictable.

As a general guideline, only use recover for a panic that you call
yourself. If you recover a panic and it's not what you expected, pass
the recovered value to a new call to panic. For example, see how the
encoding/json or text/template packages handle recovering panics.

Ian

Max Claus

unread,
Dec 8, 2025, 6:18:46 PM (2 days ago) Dec 8
to golang-nuts
Thanks, Ian, this is very helpful.

I’ll open an issue in the Go repository suggesting that the net/http documentation be made more explicit: although the HTTP server has built-in panic recovery, it should not be treated as a best practice pattern. The general guidance should be to let unexpected panics crash the program, unless explicitly recovering from a panic you triggered and fully understand.

Do you know if there is an official Go document that states this clearly? It seems like an important detail to have in formal documentation rather than buried in discussion threads, which can be harder to find and consume.

I did find a few places discussing this, but they all seem to be personal blog posts or discussion threads:
https://github.com/golang/go/issues/25245
https://iximiuz.com/en/posts/go-http-handlers-panic-and-deadlocks/
https://eli.thegreenplace.net/2018/on-the-uses-and-misuses-of-panics-in-go/
https://groups.google.com/g/golang-dev/c/rXs4TG1gdXw/m/7BQ29S4NPrgJ

Thanks again for the clarification.

Robert Engels

unread,
Dec 8, 2025, 6:29:43 PM (2 days ago) Dec 8
to Ian Lance Taylor, Max Claus, golang-nuts
Hi Ian. Can you add more detail on “leave data structures and locks in an inconsistent state”. Isn’t that the purpose of defer - especially in the context of code that may panic - to ensure that is not the case?

-- 
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcVLmE9KxnYTC1rbJHE9E1WHpSdGsVwHDT2CH%3DfK_2ZoGQ%40mail.gmail.com.

Ian Lance Taylor

unread,
Dec 8, 2025, 7:35:59 PM (2 days ago) Dec 8
to Max Claus, golang-nuts
On Mon, Dec 8, 2025 at 3:24 PM Max Claus <maxc...@gmail.com> wrote:
>
> Thanks, Ian, this is very helpful.
>
> I’ll open an issue in the Go repository suggesting that the net/http documentation be made more explicit: although the HTTP server has built-in panic recovery, it should not be treated as a best practice pattern. The general guidance should be to let unexpected panics crash the program, unless explicitly recovering from a panic you triggered and fully understand.
>
> Do you know if there is an official Go document that states this clearly? It seems like an important detail to have in formal documentation rather than buried in discussion threads, which can be harder to find and consume.

I think the clearest statement of what we consider to be good Go style
may be https://go.dev/wiki/PanicAndRecover.

You also already mentioned the Google Go style guide.

Ian

Ian Lance Taylor

unread,
Dec 8, 2025, 7:43:34 PM (2 days ago) Dec 8
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025 at 3:23 PM Robert Engels <rob...@me.com> wrote:
>
> Hi Ian. Can you add more detail on “leave data structures and locks in an inconsistent state”. Isn’t that the purpose of defer - especially in the context of code that may panic - to ensure that is not the case?

Yes, defer can indeed be used that way. Still, I believe what I said
is true: Go code in practice does not attempt to be safe in the
presence of panics in code that it calls. I will stress "in practice."

Ian

Robert Engels

unread,
Dec 8, 2025, 8:21:59 PM (2 days ago) Dec 8
to Ian Lance Taylor, Max Claus, golang-nuts
Hmmm. Seems like there shouldn't be a panic and recover in the language then - just always abort - or things are too risky. Or make it a private stdlib localized/internal capability.

Max Claus

unread,
Dec 8, 2025, 8:37:33 PM (2 days ago) Dec 8
to golang-nuts
Unfortunately, the PanicAndRecover post does not cover this specific guidance, that panic recovery should only be used for known panics that are owned and controlled by the program or library itself. As a result, the Google style guide is the closest thing we have to an official public document that shortly covers this behavior.

Ian Lance Taylor

unread,
Dec 8, 2025, 10:08:14 PM (2 days ago) Dec 8
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025, 5:15 PM Robert Engels <rob...@me.com> wrote:
Hmmm. Seems like there shouldn't be a panic and recover in the language then - just always abort - or things are too risky. Or make it a private stdlib localized/internal capability.

There is no problem with recovering your own calls to panic, and that is a useful technique. It is used by, for example, encoding/json.

For that matter recovering a panic to log additional information can also be useful, as long as you don't try to continue executing normally.

Robert Engels

unread,
Dec 8, 2025, 10:18:12 PM (2 days ago) Dec 8
to Ian Lance Taylor, Max Claus, golang-nuts
But how can you manage that in a library situation? The is no “throws” type declaration, so how can you be certain that the panic is actually raised by your code if you’re calling library routines in the same function? I guess you could wrap every call but that seems tedious - so it seems more “you can’t use panic/recover if you call ANY code not your own” - which also seems difficult to ensure.

Wouldn’t it be better (at least safer) to make the standard that you need to use defer to ensure resources are clean as ANY line might panic? or you need to run your app in a “any panic crashes mode” - which again seems problematic for internal library code.

Anyway I figure the ship has sailed but this seems an unfortunate gap.

Ian Lance Taylor

unread,
Dec 8, 2025, 10:52:52 PM (2 days ago) Dec 8
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025, 7:12 PM Robert Engels <rob...@me.com> wrote:
But how can you manage that in a library situation? The is no “throws” type declaration, so how can you be certain that the panic is actually raised by your code if you’re calling library routines in the same function? I guess you could wrap every call but that seems tedious - so it seems more “you can’t use panic/recover if you call ANY code not your own” - which also seems difficult to ensure.

You call panic with a value of a type that you define, and you check for that type when you call recover. See encoding/json or text/template.


Wouldn’t it be better (at least safer) to make the standard that you need to use defer to ensure resources are clean as ANY line might panic? or you need to run your app in a “any panic crashes mode” - which again seems problematic for internal library code.

Sure, that is ideal, but it's frankly pointless to expect everybody to write Go code like that. People empirically can't write C++ code like that, and C++ has more support for it than Go does.

Style recommendations need to be achievable. 

Ian

Robert Engels

unread,
Dec 8, 2025, 11:05:50 PM (2 days ago) Dec 8
to Ian Lance Taylor, Max Claus, golang-nuts
I don’t agree with that. The entire premise of RAII in C++ is to ensure the proper management of resources in the context of exceptions. Sure you can write code that doesn’t do that but it would be incorrect (and difficult to debug). 

On Dec 8, 2025, at 9:46 PM, Ian Lance Taylor <ia...@golang.org> wrote:



Ian Lance Taylor

unread,
Dec 9, 2025, 12:14:03 AM (yesterday) Dec 9
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025, 7:59 PM Robert Engels <rob...@me.com> wrote:
I don’t agree with that. The entire premise of RAII in C++ is to ensure the proper management of resources in the context of exceptions. Sure you can write code that doesn’t do that but it would be incorrect (and difficult to debug). 

We must agree to disagree. It takes a lot more than RAII.

Robert Engels

unread,
Dec 9, 2025, 12:29:11 AM (yesterday) Dec 9
to Ian Lance Taylor, Max Claus, golang-nuts
Fair enough. If you find the time I’d love a response or a link to some details that supports your position. I’ll readily agree that RAII is not used universally due to some performance concerns (like using shared_ptr everywhere) but in concept I believe it solves the dandling resources concern with exceptions. Java takes a different approach with try/finally (which I consider close enough to defer in Go)

Or, just enjoy the holidays :)

Ian Lance Taylor

unread,
Dec 9, 2025, 1:12:31 AM (yesterday) Dec 9
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025, 9:22 PM Robert Engels <rob...@me.com> wrote:
Fair enough. If you find the time I’d love a response or a link to some details that supports your position. I’ll readily agree that RAII is not used universally due to some performance concerns (like using shared_ptr everywhere) but in concept I believe it solves the dandling resources concern with exceptions. Java takes a different approach with try/finally (which I consider close enough to defer in Go)


See, for example, Herb Sutter's Exceptional C++, which comes with answers but also shows the complexities. You can get the flavor from https://herbsutter.com/gotw/_102/ .

Ian

Robert Engels

unread,
Dec 9, 2025, 1:47:01 AM (yesterday) Dec 9
to Ian Lance Taylor, Max Claus, golang-nuts
As that article points out, it is a combination of C++ expression evaluation order and RAII. If you understand the expression evaluation rules you can write 100% resource safe code using RAII.

That is also an example of why C++ is not the easiest language to deal with - because the rules are vast and not intuitive (coupled with undefined behavior) - thus the popularity of languages like Go.

So, given that exceptions are an “exceptional condition” - I think even a “basic" use of RAII is going to leave the program in a consistent state (with a possible memory leak). There also have smart pointer constructors specifically designed to avoid this problem built into the stdlib.

So I don’t think claiming RAII doesn’t work while saying "only use panic/recover if you control all of the code” means Go is superior in this regard.

Which is harder to implement/enforce in real world scenarios?

Robert Engels

unread,
Dec 9, 2025, 7:11:48 AM (yesterday) Dec 9
to Ian Lance Taylor, Max Claus, golang-nuts
One other point, you can’t even write the code in the article in Go. If either of the expression function could fail (returning errors - irrespective of panic/recover) as there is no way to check the errors - so the only possible error exit would be an internal exception like a bounds exception which according to the guidelines you can’t/shouldn't trap anyway. 

So if you have to break the expression up into multiple statements you could do the same thing in C++ avoiding any RAII issues - and also allows using defer to avoid state errors with panic/recover in Go. 

On Dec 9, 2025, at 12:39 AM, Robert Engels <rob...@me.com> wrote:

As that article points out, it is a combination of C++ expression evaluation order and RAII. If you understand the expression evaluation rules you can write 100% resource safe code using RAII.

Ian Lance Taylor

unread,
Dec 9, 2025, 1:23:08 PM (23 hours ago) Dec 9
to Robert Engels, Max Claus, golang-nuts
On Mon, Dec 8, 2025 at 10:40 PM Robert Engels <rob...@me.com> wrote:
>
> As that article points out, it is a combination of C++ expression evaluation order and RAII. If you understand the expression evaluation rules you can write 100% resource safe code using RAII.
>
> That is also an example of why C++ is not the easiest language to deal with - because the rules are vast and not intuitive (coupled with undefined behavior) - thus the popularity of languages like Go.
>
> So, given that exceptions are an “exceptional condition” - I think even a “basic" use of RAII is going to leave the program in a consistent state (with a possible memory leak). There also have smart pointer constructors specifically designed to avoid this problem built into the stdlib.
>
> So I don’t think claiming RAII doesn’t work while saying "only use panic/recover if you control all of the code” means Go is superior in this regard.

To be clear, I am not making that argument. And, to be clear, I am not
saying that there is anything wrong with C++, or that Go is superior
to C++.

Ian

Robert Engels

unread,
Dec 9, 2025, 2:06:30 PM (22 hours ago) Dec 9
to Ian Lance Taylor, Max Claus, golang-nuts
To be clear, I never said you said (or tried to imply) there was anything wrong with C++ or that Go was superior. I was making the claim that RAII solves the problem and that it isn’t that complex. I reread my email and the last sentence didn’t come off as intended. I was trying to respond to the “it takes more than RAII” claim and it came out wrong. My bad and I apologize.
Reply all
Reply to author
Forward
0 new messages