From 43e6958fa32eb3eadf7988c4a8f4da857650738a Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 18 Feb 2026 03:29:07 +0000 Subject: [PATCH] Recover from panics in parallel executor workers (#153) The worker goroutines in `NewParallelExecutor` had no panic recovery. A panic in any executor (e.g. expression evaluation) would crash the entire runner process, leaving all steps stuck in the UI because the final status was never reported back. Add `defer`/`recover` in the worker goroutines to convert panics into errors that propagate through the normal error channel. Issue is present in latest `nektos/act` as well. Fixes https://gitea.com/gitea/act_runner/issues/371 (Partially generated by Claude) Reviewed-on: https://gitea.com/gitea/act/pulls/153 Reviewed-by: Lunny Xiao Co-authored-by: silverwind Co-committed-by: silverwind --- pkg/common/executor.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/common/executor.go b/pkg/common/executor.go index 1150c894..38595c3d 100644 --- a/pkg/common/executor.go +++ b/pkg/common/executor.go @@ -3,6 +3,7 @@ package common import ( "context" "fmt" + "runtime/debug" log "github.com/sirupsen/logrus" ) @@ -110,7 +111,18 @@ func NewParallelExecutor(parallel int, executors ...Executor) Executor { for executor := range work { taskCount++ log.Debugf("Worker %d executing task %d", workerID, taskCount) - errs <- executor(ctx) + // Recover from panics in executors to avoid crashing the worker + // goroutine which would leave the runner process hung. + // https://gitea.com/gitea/act_runner/issues/371 + errs <- func() (err error) { + defer func() { + if r := recover(); r != nil { + log.Errorf("panic in executor: %v\n%s", r, debug.Stack()) + err = fmt.Errorf("panic: %v", r) + } + }() + return executor(ctx) + }() } log.Debugf("Worker %d finished (%d tasks executed)", workerID, taskCount) }(i, work, errs)