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

  1. Pass a non-nil body step to Loop; check the variable before calling.
  2. Provide a fallback/no-op step when the body may legitimately be absent.
  3. 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

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


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/0562dcda510f8272. Report an issue: GitHub.