Reproduction example:
package main
func noop() {}
var block = make(chan bool)
func recoverAndBlock() {
defer noop()
err := recover()
println("recovered:", err)
block <- true
println("unblocked")
}
func handle() {
defer noop()
panic("expected panic")
}
func serve() {
defer recoverAndBlock()
handle()
panic("this line must never execute")
}
func main() {
go func() {
println("awaiting cookie")
<-block
println("got cookie")
}()
serve()
}
Under GopherJS panic("this line must never execute") gets executed despite the fact panic recovery happens in the deferred function of serve(). The exact sequence of events is hard to summarize, but in short the first panic() runs deferred function and eventually recovers. However, because recoverAndBlock() defers another function, GopherJS runtime incorrectly determines call stack level where panic got recovered and continues execution of serve().