From 691d9a6e905261650c2aa44e56808d3903240554 Mon Sep 17 00:00:00 2001 From: "m.huber" Date: Sun, 25 Jan 2026 16:29:35 +0100 Subject: [PATCH 1/4] local backend: getXstate check for nil val --- pipeline/backend/local/local.go | 38 ++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index 9a8716e4b2a..c6400be0fe9 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -217,13 +217,20 @@ func (e *local) DestroyStep(_ context.Context, step *types.Step, taskUUID string // As WaitStep can not use cmd.Wait() witch ensures the process already finished and // the io pipe is closed on process end, we make sure it is done. - _ = state.output.Close() - state.output = nil - _ = state.cmd.Cancel() - state.cmd = nil - workflowState, _ := e.getWorkflowState(taskUUID) - workflowState.stepState.Delete(step.UUID) + if state.output != nil { + _ = state.output.Close() + state.output = nil + } + if state.cmd != nil { + _ = state.cmd.Cancel() + state.cmd = nil + } + workflowState, err := e.getWorkflowState(taskUUID) + if err != nil { + return err + } + workflowState.stepState.Delete(step.UUID) return nil } @@ -237,11 +244,16 @@ func (e *local) DestroyWorkflow(_ context.Context, _ *types.Config, taskUUID str // clean up steps not cleaned up because of context cancel or detached function state.stepState.Range(func(_, value any) bool { - state, _ := value.(*stepState) - _ = state.output.Close() - state.output = nil - _ = state.cmd.Cancel() - state.cmd = nil + if state, ok := value.(*stepState); ok && state != nil { + if state.output != nil { + _ = state.output.Close() + state.output = nil + } + if state.cmd != nil { + _ = state.cmd.Cancel() + state.cmd = nil + } + } return true }) @@ -264,7 +276,7 @@ func (e *local) getWorkflowState(taskUUID string) (*workflowState, error) { } s, ok := state.(*workflowState) - if !ok { + if !ok || s == nil { return nil, fmt.Errorf("could not parse state: %v", state) } @@ -283,7 +295,7 @@ func (e *local) getStepState(taskUUID, stepUUID string) (*stepState, error) { } s, ok := state.(*stepState) - if !ok { + if !ok || s == nil { return nil, fmt.Errorf("could not parse state: %v", state) } From 069b3d213abf3c70f6b5403dabaa13cf08431d6d Mon Sep 17 00:00:00 2001 From: "m.huber" Date: Sun, 25 Jan 2026 16:42:04 +0100 Subject: [PATCH 2/4] local backend: handle canceled steps case --- pipeline/backend/local/local.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index c6400be0fe9..69a425f097e 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -179,6 +179,14 @@ func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) ( return nil, err } + if state.cmd == nil { + return nil, errors.New("exec: step command not set up") + } + + stepState := &types.State{ + Exited: true, + } + // normally we use cmd.Wait() to wait for *exec.Cmd, but cmd.StdoutPipe() tells us not // as Wait() would close the io pipe even if not all logs where read and send back // so we have to do use the underlying functions @@ -190,7 +198,14 @@ func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) ( if err != nil { return nil, err } - state.cmd.ProcessState = cmdState + if cmdState == nil { + return nil, errors.New("exec: cmd state after Wait() can not be nil but is") + } + stepState.ExitCode = cmdState.ExitCode() + // can be nil if step got canceled + if state.cmd != nil { + state.cmd.ProcessState = cmdState + } } return &types.State{ From 647f97d6d1c3cf14b72b8ced59dc6fab8af5dd85 Mon Sep 17 00:00:00 2001 From: "m.huber" Date: Sun, 25 Jan 2026 16:47:52 +0100 Subject: [PATCH 3/4] local backend: handle canceled steps case (2) --- pipeline/backend/local/local.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index 69a425f097e..6a2641e84ae 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -208,10 +208,7 @@ func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) ( } } - return &types.State{ - Exited: true, - ExitCode: state.cmd.ProcessState.ExitCode(), - }, err + return stepState, err } func (e *local) TailStep(_ context.Context, step *types.Step, taskUUID string) (io.ReadCloser, error) { From c8c7626d3939d01a761dacb26008f28b1a6805a4 Mon Sep 17 00:00:00 2001 From: "m.huber" Date: Sun, 25 Jan 2026 17:04:07 +0100 Subject: [PATCH 4/4] local backend: add handle edge-edge case --- pipeline/backend/local/local.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index 6a2641e84ae..b6c769d0a12 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -206,6 +206,8 @@ func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) ( if state.cmd != nil { state.cmd.ProcessState = cmdState } + } else { + stepState.ExitCode = state.cmd.ProcessState.ExitCode() } return stepState, err