When using io.Copy with an invalid writer, I get a panic - that's expected. However I can't recover when this is the case. My deferred recover is completely bypassed. Here is the code:
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/debug"
"cloud.google.com/go/storage"
)
func main() {
var (
ctx = context.Background()
fromFile = "blah.txt"
bucket = "blah-bucket"
path = "blah-path"
)
defer func() {
if result := recover(); result != nil {
buf := make([]byte, 1<<16)
length := runtime.Stack(buf, false)
log.Fatalf("PANIC RECOVER: %v\nSTACK: \n%s", result, buf[:length])
debug.PrintStack()
}
}()
err := FakeUpload(ctx, fromFile, bucket, path)
if err != nil {
fmt.Println(err)
}
fmt.Println("HELLO")
}
func FakeUpload(ctx context.Context, fromFile, toBucket, toPath string) (err error) {
var (
file *os.File
client *storage.Client
wc *storage.Writer
)
defer func() {
for _, c := range []io.Closer{wc, file} {
if c != nil {
err = c.Close()
if err != nil {
return
}
}
}
}()
file, err = os.Open(fromFile)
if err != nil {
err = fmt.Errorf("problem opening file %v: %v", fromFile, err)
return
}
wc = client.Bucket(toBucket).Object(toPath).NewWriter(ctx)
_, err = io.Copy(wc, file) // THE UNRECOVERABLE PANIC HAPPENS HERE
return
}
The panic being:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x9e0fa8]
goroutine 21 [running]:
cloud.google.com/go/storage.(*Writer).open.func1(0xc000161200, 0xc0002721e0, 0xc000150388, 0xc000290e20, 0x1, 0x1)
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:128 +0x248
created by cloud.google.com/go/storage.(*Writer).open
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:118 +0x6ce
Process exiting with code: 0
module main
go 1.15
require cloud.google.com/go/storage v1.14.0
go version go1.15.10 windows/amd64
The kicker is, if it panics elsewhere, for example I point to an invalid file, it'll panic, and the deferred recover properly captures it.
This has me baffled. Any idea?
The answer, as you can read in the comments above, is that cloud.google.com/go/storage
writer is creating a goroutine, and throwing the panic in there. GO does not allow you to recover from another goroutine.