micro/go-micro · error
flow: Loop requires a body step
Error message
flow: Loop requires a body step
What it means
The Loop step combinator was created with a nil body step, so when the returned step function runs it immediately returns this error instead of executing zero iterations. The library guards against a nil body because looping nothing is always a programming mistake.
Source
Thrown at flow/loop.go:84
// ),
// )
//
// The loop runs as a single flow step: the flow checkpoints the loop's
// outcome, and a resume re-enters the step, so loop bodies should be safe to
// repeat. Use OnIteration to record per-pass progress. If the cap is hit
// before the stop condition fires, the loop returns the latest state rather
// than erroring — the guardrail did its job.
func Loop(body StepFunc, opts ...LoopOption) StepFunc {
o := LoopOptions{Max: 10}
for _, op := range opts {
op(&o)
}
if o.Max <= 0 {
o.Max = 10
}
return func(ctx context.Context, in State) (State, error) {
if body == nil {
return in, fmt.Errorf("flow: Loop requires a body step")
}
cur := in
for iter := 1; iter <= o.Max; iter++ {
out, err := body(ctx, cur)
if err != nil {
return cur, fmt.Errorf("loop iteration %d: %w", iter, err)
}
cur = out
if o.OnIter != nil {
o.OnIter(iter, cur)
}
done, err := loopDone(ctx, o, cur, iter)
if err != nil {
return cur, err
}
if done {
return cur, nil
}View on GitHub (pinned to 24529f1404)
Solutions
- Pass a non-nil body step to Loop; check the variable before calling.
- Provide a fallback/no-op step when the body may legitimately be absent.
- If steps come from config, validate that the body step is defined before constructing the pipeline.
Example fix
// before
var body flow.Step // left nil
out, err := flow.Loop(flow.Max(5))(ctx, in) // body nil
// after
body := flow.StepFunc(func(ctx context.Context, in flow.State) (flow.State, error) { return in, nil })
out, err := flow.Loop(flow.Max(5))(ctx, in) Defensive patterns
Strategy: validation
Validate before calling
func safeLoop(opts ...flow.LoopOption, body flow.Step) flow.Step {
if body == nil { body = flow.StepFunc(func(ctx context.Context, in flow.State) (flow.State, error) { return in, nil }) }
return flow.Loop(opts...)(nil, body)
} Type guard
func isNilStep(s flow.Step) bool { return s == nil || reflect.ValueOf(s).IsNil() } Try / catch
out, err := loopStep(ctx, in)
if err != nil && strings.Contains(err.Error(), "requires a body step") {
return fmt.Errorf("pipeline misconfigured: loop body was nil: %w", err)
} Prevention
- Never pass function-typed step variables without checking for nil first.
- Build pipelines through constructors that reject nil steps eagerly.
- Unit-test pipeline construction with every config permutation.
When it happens
Trigger: Using flow.Loop with a body argument that is nil — e.g. a conditionally-built step variable left nil, or passing a function-typed field that was never assigned.
Common situations: Building steps dynamically where an if-branch forgot to assign the body; refactoring that renamed a variable leaving a nil step; unmarshalling pipeline definitions where the body step is missing.
Related errors
- loop iteration %d: %w
- flow: LLMOptimizer requires a model
- flow: LLMOptimizer returned an empty prompt
- unknown provider: %s
- subscribe to %s: %w
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/0562dcda510f8272.
Report an issue: GitHub.