jd-opensource/joyagent-jdgenie · error · IllegalStateException
No plan exists. Create a plan first.
Error message
No plan exists. Create a plan first.
What it means
PlanningTool maintains an in-memory plan object for an agent session. updatePlan throws this IllegalStateException when plan update/revise is invoked but no plan was ever created via the create command. The tool enforces plan lifecycle ordering: create must precede update and mark_step.
Solutions
- Have the agent call the create command first before issuing update_plan.
- Auto-create a default plan in updatePlan if plan == null (defensive fallback).
- Persist the plan across tool instances/sessions instead of keeping it in a field.
Example fix
// before
if (plan == null) {
throw new IllegalStateException("No plan exists. Create a plan first.");
}
// after
if (plan == null) {
plan = new Plan(title, steps); // auto-create
return "我已创建plan";
}
plan.update(title, steps); Defensive patterns
Strategy: validation
Validate before calling
// caller-side check before invoking update
if (!planningTool.hasPlan()) {
planningTool.execute(Map.of("command", "create", "title", t, "steps", steps));
} Try / catch
try {
tool.execute(params);
} catch (IllegalStateException e) {
if (e.getMessage().contains("No plan exists")) {
tool.execute(createParams); // then retry update
} else throw e;
} Prevention
- Enforce create-before-update in the agent prompt/state machine.
- Persist plan state per session id rather than in a tool field.
- Expose a hasPlan() query so callers can pre-check.
When it happens
Trigger: Calling PlanningTool with command=update (or any non-create command) when the internal `plan` field is null — i.e., `create` was never invoked in this session, or a new tool/agent instance replaced the one holding the plan.
Common situations: Agent LLM emits an update_plan action before ever creating a plan; agent restart or session switch loses the in-memory plan while the prompt history still shows plan steps; multiple PlanningTool instances each holding separate state.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- step_index is required for mark_step command
- Tool execution failed: " + error
- Tool execution result is null
- Input must be a Map
- Command is required
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/f10d99a8eb847186.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/agent/tool/common/PlanningTool.java:161
if (title == null || steps == null) {
throw new IllegalArgumentException("title, and steps are required for create command");
}
if (plan != null) {
throw new IllegalStateException("A plan already exists. Delete the current plan first.");
}
plan = Plan.create(title, steps);
return "我已创建plan";
}
private String updatePlan(Map<String, Object> params) {
String title = (String) params.get("title");
List<String> steps = (List<String>) params.get("steps");
if (plan == null) {
throw new IllegalStateException("No plan exists. Create a plan first.");
}
plan.update(title, steps);
return "我已更新plan";
}
private String markStep(Map<String, Object> params) {
Integer stepIndex = (Integer) params.get("step_index");
String stepStatus = (String) params.get("step_status");
String stepNotes = (String) params.get("step_notes");
if (plan == null) {
throw new IllegalStateException("No plan exists. Create a plan first.");
}
if (stepIndex == null) {
throw new IllegalArgumentException("step_index is required for mark_step command");
}View on GitHub (pinned to 2417e0b8b6)