micro/go-micro · error
agent run %s has unfinished plan steps: %s
Error message
agent run %s has unfinished plan steps: %s
What it means
Before continuing a checkpointed run, the agent checks for unfinished plan steps (agent/agent.go:552). If any remain, it marks the run failed, saves state so a later resume restarts from the saved input message, and returns this error listing the unfinished step names. It prevents skipping half-completed plan work.
Source
Thrown at agent/agent.go:552
reply += "\n\n"
}
reply += resp.Answer
}
completedToolCalls := checkpointToolCalls(run.Steps)
if a.currentRun != nil {
completedToolCalls = checkpointToolCalls(a.currentRun.Steps)
}
res := &Response{
Reply: reply,
ToolCalls: mergeCheckpointToolCalls(completedToolCalls, resp.ToolCalls),
Agent: a.opts.Name,
RunID: a.runID,
ParentID: parentRunID,
}
if a.opts.Checkpoint != nil {
if unfinished := a.unfinishedPlanSteps(); len(unfinished) > 0 {
err = fmt.Errorf("agent run %s has unfinished plan steps: %s", run.ID, strings.Join(unfinished, ", "))
run.Status = "failed"
run.State.Stage = agentAskStep
run.State.Data = []byte(message)
if a.currentRun != nil {
run.Steps = a.currentRun.Steps
}
if len(run.Steps) == 0 {
run.Steps = []flow.StepRecord{{Name: agentAskStep}}
}
run.Steps[0].Status = "failed"
run.Steps[0].Error = err.Error()
_ = a.saveRun(ctx, run)
return nil, err
}
}
run.Status = "done"
run.State.Stage = ""
if b, marshalErr := json.Marshal(res); marshalErr == nil {View on GitHub (pinned to 24529f1404)
Solutions
- Call agent.Resume(ctx, ag, runID) to continue the interrupted plan instead of starting a new Ask
- Complete or cancel the outstanding plan steps, then retry the request
- Use a fresh agent/run (or clear the checkpoint) if the old plan should be abandoned
- Serialize access to the agent — avoid concurrent Ask calls on the same checkpointed agent
- Inspect the listed step names in the error to see exactly which work is pending
Example fix
// before
resp, err := ag.Ask(ctx, newMsg) // unfinished plan steps error
// after
resp, err := agent.Resume(ctx, ag, lastRunID) // continue existing plan
if err != nil {
resp, err = ag.Ask(ctx, newMsg) // only after plan resolved
}
Defensive patterns
Strategy: validation
Validate before calling
// before Ask, ensure no unfinished plan steps on the last checkpointed run
runs, err := agent.Pending(ctx, ag)
if err == nil && len(runs) > 0 {
// resume those runs first instead of calling Ask
} Try / catch
resp, err := ag.Ask(ctx, msg)
if err != nil && strings.Contains(err.Error(), "unfinished plan steps") {
// finish or cancel the plan, or resume the run, then retry
return recoverPlan(err)
} Prevention
- Resume interrupted runs before issuing new Ask calls
- Serialize Ask calls per checkpointed agent
- After crashes, drain pending runs at startup with ResumePending
- Clear/cancel abandoned plans explicitly before starting fresh work
When it happens
Trigger: Calling Ask/Chat on an agent with Checkpoint configured while a previous run's plan has steps not yet completed (unfinishedPlanSteps() non-empty); also surfaced in streaming tests like TestA2AStreamUsesAgentChatPathWithTools.
Common situations: Reusing an agent mid-plan without resuming properly, a crashed prior run leaving plan steps open, concurrent Ask calls on the same checkpointed agent, or resetting memory/checkpoint without clearing plan state.
Related errors
- agent: ResumeStreamAsk unsupported by implementation
- agent: ResumeStreamAsk requires a checkpoint
- agent: checkpointed run not found
- agent: checkpointed run is terminal with status
- agent: checkpointed run is input-required; resume with Resum
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/390d03e7a37327ff.
Report an issue: GitHub.