{"record":{"id":"26e450a2699e4c31","repo":"nektos/act","slug":"error-occurred-running-finally-v-original-error","errorCode":null,"errorMessage":"Error occurred running finally: %v (original error: %v)","messagePattern":"Error occurred running finally: (.+?) \\(original error: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/common/executor.go","lineNumber":230,"sourceCode":"\t\t}\n\t\treturn nil\n\t}\n}\n\n// IfBool only runs this executor if conditional is true\nfunc (e Executor) IfBool(conditional bool) Executor {\n\treturn e.If(func(_ context.Context) bool {\n\t\treturn conditional\n\t})\n}\n\n// Finally adds an executor to run after other executor\nfunc (e Executor) Finally(finally Executor) Executor {\n\treturn func(ctx context.Context) error {\n\t\terr := e(ctx)\n\t\terr2 := finally(ctx)\n\t\tif err2 != nil {\n\t\t\treturn fmt.Errorf(\"Error occurred running finally: %v (original error: %v)\", err2, err)\n\t\t}\n\t\treturn err\n\t}\n}\n\n// Not return an inverted conditional\nfunc (c Conditional) Not() Conditional {\n\treturn func(ctx context.Context) bool {\n\t\treturn !c(ctx)\n\t}\n}\n","sourceCodeStart":212,"sourceCodeEnd":242,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/common/executor.go#L212-L242","documentation":"Executor.Finally wraps a main executor and a finally-executor: it runs both and, if the finally itself errors, returns this composite error carrying both the finally error and the original (possibly nil) error. Note the quirk: even when the main executor succeeded (original error is nil), a failing finally turns the whole chain into an error — masking success. The message uses %v, so the original error's type/unwrap chain is lost.","triggerScenarios":"Any runner cleanup stage wrapped in .Finally() failing: container stop/remove errors, artifact/close errors, log upload failures — occurring after the job's main steps, whether or not those steps failed.","commonSituations":"Post-job cleanup racing with Docker daemon shutdown; container already removed by a timeout so 'docker rm' fails; cleanup code returning errors on nil-safe paths; the failure appearing only when the job itself is green, confusing CI triage.","solutions":["Read the two values in the message: fix the finally error first; the '(original error: <nil>)' suffix tells you the job steps themselves succeeded","Make finally executors best-effort: log-and-swallow non-essential cleanup errors instead of returning them","If cleanup must fail loudly, keep it, but ensure the original error is preserved (e.g. use errors.Join or fmt.Errorf with two %w)","Reproduce with act -v to see which finally stage emitted the error"],"exampleFix":"// before: cleanup error masks a successful job\nerr := step.Finally(func(ctx context.Context) error {\n    return container.Remove(ctx) // returns error -> whole executor fails\n})(ctx)\n\n// after: best-effort finally\nerr := step.Finally(func(ctx context.Context) error {\n    if rerr := container.Remove(ctx); rerr != nil {\n        common.Logger(ctx).WithError(rerr).Warn(\"cleanup failed\")\n    }\n    return nil\n})(ctx)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := pipeline.Finally(cleanup)(ctx)\nif err != nil && strings.Contains(err.Error(), \"Error occurred running finally\") {\n    // decide policy: a cleanup failure after a green job usually should not fail CI\n    if strings.HasSuffix(err.Error(), \"(original error: <nil>)\") {\n        log.Warnf(\"cleanup failed but steps succeeded: %v\", err)\n        err = nil // or escalate, per policy\n    }\n}","preventionTips":["Make finally executors best-effort: log and return nil unless cleanup is load-bearing","Never let cleanup errors mask the original failure — carry both values explicitly","Test the finally path separately from the happy path"],"tags":["executor","cleanup","finally","control-flow","act"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}