{"record":{"id":"f10d99a8eb847186","repo":"jd-opensource/joyagent-jdgenie","slug":"no-plan-exists-create-a-plan-first","errorCode":null,"errorMessage":"No plan exists. Create a plan first.","messagePattern":"No plan exists\\. Create a plan first\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"genie-backend/src/main/java/com/jd/genie/agent/tool/common/PlanningTool.java","lineNumber":161,"sourceCode":"\n        if (title == null || steps == null) {\n            throw new IllegalArgumentException(\"title, and steps are required for create command\");\n        }\n\n        if (plan != null) {\n            throw new IllegalStateException(\"A plan already exists. Delete the current plan first.\");\n        }\n\n        plan = Plan.create(title, steps);\n        return \"我已创建plan\";\n    }\n\n    private String updatePlan(Map<String, Object> params) {\n        String title = (String) params.get(\"title\");\n        List<String> steps = (List<String>) params.get(\"steps\");\n\n        if (plan == null) {\n            throw new IllegalStateException(\"No plan exists. Create a plan first.\");\n        }\n\n        plan.update(title, steps);\n        return \"我已更新plan\";\n    }\n\n    private String markStep(Map<String, Object> params) {\n        Integer stepIndex = (Integer) params.get(\"step_index\");\n        String stepStatus = (String) params.get(\"step_status\");\n        String stepNotes = (String) params.get(\"step_notes\");\n\n        if (plan == null) {\n            throw new IllegalStateException(\"No plan exists. Create a plan first.\");\n        }\n\n        if (stepIndex == null) {\n            throw new IllegalArgumentException(\"step_index is required for mark_step command\");\n        }","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/jd-opensource/joyagent-jdgenie/blob/2417e0b8b636d941ad5fb14c59b20dddfef5375d/genie-backend/src/main/java/com/jd/genie/agent/tool/common/PlanningTool.java#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (plan == null) {\n    throw new IllegalStateException(\"No plan exists. Create a plan first.\");\n}\n// after\nif (plan == null) {\n    plan = new Plan(title, steps); // auto-create\n    return \"我已创建plan\";\n}\nplan.update(title, steps);","handlingStrategy":"validation","validationCode":"// caller-side check before invoking update\nif (!planningTool.hasPlan()) {\n    planningTool.execute(Map.of(\"command\", \"create\", \"title\", t, \"steps\", steps));\n}","typeGuard":null,"tryCatchPattern":"try {\n    tool.execute(params);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"No plan exists\")) {\n        tool.execute(createParams); // then retry update\n    } else throw e;\n}","preventionTips":["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."],"tags":["java","illegal-state","agent-tooling","plan-lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"2417e0b8b636d941ad5fb14c59b20dddfef5375d","analyzedAt":"2026-09-08T11:28:19.414Z","contentChangedAt":"2026-09-08T11:28:19.414Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}