Горутина не может вернуть ошибку вызывающему коду — нужен механизм передачи. Самый чистый способ — errgroup:
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return doWork(ctx)
})
if err := g.Wait(); err != nil {
// обработка
}
errgroup ждёт все горутины и возвращает первую ошибку. Альтернативы: канал ошибок или структура с результатом и ошибкой.
Если горутина паникует — recover через defer, иначе упадёт весь процесс. Паника в горутине не перехватывается в родительской горутине.