panic call blocks indefinitely, does not exit program

48 views
Skip to first unread message

Andrew Pillar

unread,
Nov 17, 2025, 6:05:12 PM (yesterday) Nov 17
to golan...@googlegroups.com
Came across this interesting problem when writing some database query
code. In summary, I wrote a function that took an interface, uses
reflect to get the struct fields from it and uses these to build up a
slice of destination values which would then be given to
database.sql.Rows.Scan. These values would be structs which implement
the sql.Scanner interface, what with their implementations using
reflect to set the value on the original struct fields.

When testing this implementation I was passing through a struct with
unexported fields, typically I would expect this to panic, as calls to
reflect.Value.Set will panic if the value cannot be set, which
unexported fields can't be. Further debugging lead me to noticing that
the call to panic causes the main goroutine to block indefinitely.

I have managed to replicate the issue in under 100 lines of code, but
this is something I have not encountered before and my searches for
"panic calls block goroutine" or such similar queries have yielded no
results for the issue I am experiencing.

The code for the replicated issue can be found here:
https://go.dev/play/p/15M8kS7i9lg

The above code has changed somewhat from the original code I had in
place for simplicites sake. On line 28 where it calls panic directly,
the original code I had instead had a call to reflect.Value.Set, which
would panic if the value could not be set. The Go playground won't be
able to run it as it times out due to the dependency on
modernc.org/sqlite (and even if it did build, the goroutine blocks
indefinitely so it won't run anyway).

I do have a workaround in place for what I'm trying to achieve, but the
question I have about this remains: Why is the call to panic blocking
the main goroutine and preventing the program from exiting?

Sean Liao

unread,
Nov 17, 2025, 7:07:07 PM (yesterday) Nov 17
to Andrew Pillar, golan...@googlegroups.com
it's something like https://github.com/golang/go/issues/64498
Scan locks but doesn't always unlock when it returns,
which causes a deadlock with your deferred close which also tries to lock.

If you run it with CGO_ENABLED=0, you'll also get a nice deadlock report:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [sync.RWMutex.Lock]:
sync.runtime_SemacquireRWMutex(0x0?, 0x0?, 0x9d8f00?)
runtime/sema.go:105 +0x25
sync.(*RWMutex).Lock(0x10?)
sync/rwmutex.go:155 +0x6b
database/sql.(*Rows).close(0x235702c1e0a0, {0x0, 0x0})
database/sql/sql.go:3452 +0xad
database/sql.(*Rows).Close(0x235702c1e0a0)
database/sql/sql.go:3448 +0x26
panic({0x70f240?, 0x7a6910?})
runtime/panic.go:856 +0x13a
main.(*field).Scan(0x9cb2d0?, {0x716700?, 0x0?})
go.seankhliao.com/testrepo1314/main.go:28 +0x25
database/sql.convertAssignRows({0x716700, 0x235702c20050}, {0x70f400,
0x7b16d0}, 0x235702c1e0a0)
database/sql/convert.go:394 +0x1fd9
database/sql.(*Rows).scanLocked(0x235702c1e0a0, {0x235702c32080, 0x4,
0x48e889?})
database/sql/sql.go:3406 +0x38c
database/sql.(*Rows).Scan(0x235702c1e0a0, {0x235702c32080, 0x4, 0x7439bc?})
database/sql/sql.go:3374 +0x73
main.main()
go.seankhliao.com/testrepo1314/main.go:72 +0x535

goroutine 17 [select]:
database/sql.(*DB).connectionOpener(0x235702c90000, {0x7a7df8, 0x235702c8a000})
database/sql/sql.go:1261 +0x89
created by database/sql.OpenDB in goroutine 1
database/sql/sql.go:841 +0x145
exit status 2

- sean

- sean
> --
> 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/f1068b9389dccbd11c13e942a0bceaec2aada8f7.camel%40andrewpillar.com.

Ian Lance Taylor

unread,
Nov 17, 2025, 7:10:24 PM (yesterday) Nov 17
to Andrew Pillar, golan...@googlegroups.com
If you hit ^\ (on a Unix system) you will get a stack backtrace from a
hung program. In this case you will see that the program is hanging in
the deferred call to db.Close in your main function. The Rows Scan
method is acquiring a lock, and db.Close is trying to get the same
lock, but nothing has released the lock. So your program is
deadlocked.

Ian
Reply all
Reply to author
Forward
0 new messages