{"record":{"id":"df9f86f74ef43108","repo":"micro/go-micro","slug":"loop-iteration-d-w","errorCode":null,"errorMessage":"loop iteration %d: %w","messagePattern":"loop iteration (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"flow/loop.go","lineNumber":90,"sourceCode":"// before the stop condition fires, the loop returns the latest state rather\n// than erroring — the guardrail did its job.\nfunc Loop(body StepFunc, opts ...LoopOption) StepFunc {\n\to := LoopOptions{Max: 10}\n\tfor _, op := range opts {\n\t\top(&o)\n\t}\n\tif o.Max <= 0 {\n\t\to.Max = 10\n\t}\n\treturn func(ctx context.Context, in State) (State, error) {\n\t\tif body == nil {\n\t\t\treturn in, fmt.Errorf(\"flow: Loop requires a body step\")\n\t\t}\n\t\tcur := in\n\t\tfor iter := 1; iter <= o.Max; iter++ {\n\t\t\tout, err := body(ctx, cur)\n\t\t\tif err != nil {\n\t\t\t\treturn cur, fmt.Errorf(\"loop iteration %d: %w\", iter, err)\n\t\t\t}\n\t\t\tcur = out\n\t\t\tif o.OnIter != nil {\n\t\t\t\to.OnIter(iter, cur)\n\t\t\t}\n\t\t\tdone, err := loopDone(ctx, o, cur, iter)\n\t\t\tif err != nil {\n\t\t\t\treturn cur, err\n\t\t\t}\n\t\t\tif done {\n\t\t\t\treturn cur, nil\n\t\t\t}\n\t\t}\n\t\treturn cur, nil\n\t}\n}\n\n// loopDone evaluates the stop conditions: a code-defined Until predicate","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/flow/loop.go#L72-L108","documentation":"The loop's body step returned an error on a given iteration; Loop wraps it with the iteration number for debugging. The wrapped error (%w) is the original body failure — this wrapper only adds context about which iteration failed.","triggerScenarios":"Running a Loop where body(ctx, cur) returns a non-nil error at iteration N — could be any failure inside the body step (model call failure, validation error, nested step error).","commonSituations":"Bodies that call LLMs hitting rate limits mid-loop; body steps failing on state produced by earlier iterations; loops with Max set too high, exhausting a downstream API.","solutions":["Unwrap the error (errors.Unwrap / %v of it) to find the root cause inside the body step.","Note the reported iteration number; add logging or an OnIter hook to inspect state at that point.","Lower Loop Max or add a done-condition so the loop exits before failure.","Make the body step idempotent/resilient (retry on transient errors) if it calls external services."],"exampleFix":"// before\nout, err := flow.Loop(flow.Max(100))(ctx, in)\n// after\nout, err := flow.Loop(flow.Max(10), flow.OnIter(func(i int, s flow.State) { log.Printf(\"iter %d: %v\", i, s) }))(ctx, in)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func loopIterErr(err error) (int, error) {\n\tvar iter int\n\tif n, e := fmt.Sscanf(err.Error(), \"loop iteration %d:\", &iter); n == 1 && e == nil { return iter, errors.Unwrap(err) }\n\treturn 0, err\n}","tryCatchPattern":"out, err := loopStep(ctx, in)\nif err != nil {\n\tvar iterErr *fmt.Errorf\n\troot := errors.Unwrap(err) // original body failure\n\tlog.Printf(\"loop failed: iteration context %v, root cause: %v\", err, root)\n\tif ai.ClassifyError(root) == ai.ErrTransient { /* retry the loop */ }\n}","preventionTips":["Make loop bodies idempotent and retry-safe.","Use OnIter to log state each iteration for debugging.","Bound Max iterations to a value your downstream services can sustain."],"tags":["flow","loop","error-wrapping"],"backgroundTag":"loop-iteration-failed","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}