micro/go-micro · error
run is waiting at unknown step
Error message
run %s is waiting at unknown step %q
What it means
ResumeWith resumes a run waiting for input by marking the awaited step done and advancing. If the run's persisted State.Stage doesn't match any step in the flow's current Steps configuration (stepIndex < 0), the library cannot identify which step the input satisfies and returns this error. It almost always means the flow definition and the persisted run state have drifted apart.
Solutions
- Restore the persisted step name in the flow's Steps so State.Stage matches again.
- Migrate the stale run: update run.State.Stage in the checkpoint store to a valid step name, or expire the run.
- Version flows (distinct flow names/checkpoint keys per definition version) so old runs resume against their matching definition.
- Drain all waiting runs via ResumeWith before deploying step renames or deletions.
Example fix
// before (deployed rename)
flow.Steps(flow.Step{Name: "review-v2", Run: review}) // runs wait at "review"
// after (keep original name)
flow.Steps(flow.Step{Name: "review", Run: review}) Defensive patterns
Strategy: validation
Validate before calling
run, _, err := ckpt.Load(ctx, runID)
if err == nil && stepIndex(f.opts.Steps, run.State.Stage) < 0 {
return fmt.Errorf("run %s waits at unknown step %q; cannot inject input", runID, run.State.Stage)
} Type guard
func stageKnown(steps []flow.Step, stage string) bool { return stepIndex(steps, stage) >= 0 } Try / catch
if _, err := f.ResumeWith(ctx, runID, input); err != nil {
if strings.Contains(err.Error(), "waiting at unknown step") {
// stale run from an old flow definition; expire or migrate it
return expireRun(ctx, runID)
}
return err
} Prevention
- Freeze step names once flows are live; add new steps instead of renaming
- Version flow definitions so waiting runs resume against their own schema
- Alert on 'unknown step' errors — they signal definition/run state drift
- Drain waiting runs before deploys that alter step names
When it happens
Trigger: Calling flow.ResumeWith(ctx, runID, input) where run.State.Stage names a step absent from f.opts.Steps — the step was renamed/removed in a deploy, or the run was created by a differently configured flow sharing the same checkpoint store.
Common situations: Deploying a new flow version with renamed steps while waiting runs persist across restarts; resuming a run with a different Flow instance than the one that started it; multiple flow definitions writing to one checkpoint store without namespacing.
Related errors
- run is not waiting for input (status )
- agent resume: unsupported agent implementation %T
- flow: duplicate step name
- flow: LLMGrader requires a flow model (set Provider/APIKey)
- flow: LLMGrader returned an empty grade
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/c73aa267cd95a6d2.
Report an issue: GitHub.
Appendix: source
Thrown at flow/steps.go:529
return err
}
if f.checkpoint == nil {
return fmt.Errorf("flow %s has no checkpoint configured", f.name)
}
run, ok, err := f.checkpoint.Load(ctx, runID)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("run %s not found", runID)
}
if run.Status != "waiting" {
return fmt.Errorf("run %s is not waiting for input (status %q)", runID, run.Status)
}
steps := f.opts.Steps
i := stepIndex(steps, run.State.Stage)
if i < 0 {
return fmt.Errorf("run %s is waiting at unknown step %q", runID, run.State.Stage)
}
// The awaited step is satisfied by the injected input; record it done and
// advance so runFrom re-enters at the next step.
run.Steps[i].Status = "done"
run.Steps[i].Result = truncate(input, 200)
run.State.Data = []byte(input)
if i+1 < len(steps) {
run.State.Stage = steps[i+1].Name
} else {
run.State.Stage = ""
}
run.Await = nil
run.Status = "running"
_, err = f.runFrom(ctx, run)
return err
}
// runFrom executes steps from the run's current Stage to the end,View on GitHub (pinned to 24529f1404)