{"record":{"id":"1d0f5233d9a5aa84","repo":"micro/go-micro","slug":"flow-step-q-has-no-run-function","errorCode":null,"errorMessage":"flow: step %q has no Run function","messagePattern":"flow: step %q has no Run function","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"flow/steps.go","lineNumber":646,"sourceCode":"\t\treturn run, err\n\t}\n\tif f.opts.DeleteOnSuccess && f.checkpoint != nil {\n\t\tif err := f.checkpoint.Delete(ctx, run.ID); err != nil {\n\t\t\tspanErr = err\n\t\t\treturn run, err\n\t\t}\n\t}\n\tf.record(resultFromRun(f.opts.TriggerTopic, run))\n\tf.log.Logf(logger.InfoLevel, \"Flow %s run %s completed (%d steps)\", f.name, run.ID, len(steps))\n\treturn run, nil\n}\n\n// runStep runs one step, retrying on error up to the resolved retry count.\n// A step with no Run function is a configuration error, and a canceled run\n// stops retrying immediately rather than burning the rest of its budget.\nfunc (f *Flow) runStep(ctx context.Context, step Step, in State) (State, int, Verification, error) {\n\tif step.Run == nil {\n\t\treturn in, 0, Verification{}, fmt.Errorf(\"flow: step %q has no Run function\", step.Name)\n\t}\n\tretries := f.opts.Retry\n\tif step.Retry > 0 {\n\t\tretries = step.Retry\n\t}\n\tvar lastErr error\n\tvar lastVerification Verification\n\tvar feedback string\n\tfor attempt := 1; attempt <= retries+1; attempt++ {\n\t\t// Stop the moment the run's context is canceled or its deadline\n\t\t// passes — a canceled run shouldn't keep retrying, and the context\n\t\t// error is surfaced so callers can detect cancellation upstream.\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn in, attempt - 1, lastVerification, err\n\t\t}\n\t\tattemptCtx := ctx\n\t\tif info, ok := ai.RunInfoFrom(ctx); ok {\n\t\t\tinfo.Step = step.Name","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/flow/steps.go#L628-L664","documentation":"Every Step in a flow must provide a Run StepFunc; a nil Run is treated as a configuration error rather than a no-op. runStep checks for this before executing and fails the step immediately with the step's name in the message. This catches flows built by leaving the Run field unset, since the library cannot infer the step's action.","triggerScenarios":"Constructing flow.Step{Name: \"x\"} (or a step struct literal) without setting Run, then starting or resuming the flow; or a helper returning a nil StepFunc assigned into a Step.","commonSituations":"Copy-pasted step definitions where the Run field was accidentally deleted; refactoring steps into a shared slice where some placeholders have nil Run; conditional step construction where a helper returned nil on some code path; JSON/YAML-driven step config that can't express function values.","solutions":["Set a Run function on every Step: Run: func(ctx context.Context, in flow.State) (flow.State, error) {...} or use a helper like flow.Call/flow.LLM.","Validate steps at startup: iterate your steps and fail fast if any has a nil Run before creating the flow.","Check helper functions for paths that return a nil StepFunc (e.g. when config is missing) and return an error or a default step instead.","If the step was a placeholder, remove it from the Steps slice."],"exampleFix":"// before\nsteps := []flow.Step{{Name: \"enrich\"}} // Run missing\n\n// after\nsteps := []flow.Step{{Name: \"enrich\", Run: func(ctx context.Context, in flow.State) (flow.State, error) {\n  return in, nil // real work here\n}}}","handlingStrategy":"validation","validationCode":"for i, s := range steps {\n    if s.Run == nil {\n        return fmt.Errorf(\"step %d (%q) has no Run function\", i, s.Name)\n    }\n}","typeGuard":"func stepRunnable(s flow.Step) bool { return s.Run != nil }","tryCatchPattern":"if _, _, err := f.runStep(ctx, step, in); err != nil {\n    if strings.Contains(err.Error(), \"has no Run function\") {\n        // configuration bug: fix the step definition, do not retry\n        return fmt.Errorf(\"misconfigured flow: %w\", err)\n    }\n    // genuine step failure: retry path applies\n}","preventionTips":["Validate all steps (non-nil Run) in a unit test over your step slice","Use flow.Call/LLM/Agent helpers instead of hand-built Step literals where possible","Check helper functions never return a nil StepFunc on error paths","Run validateSteps at service startup to fail fast on bad definitions"],"tags":["go","flow","configuration","validation"],"backgroundTag":"missing-required-callback","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}