{"record":{"id":"cd2d0e0d5163be67","repo":"HMCL-dev/HMCL","slug":"multimc-installation-draft-is-not-open","errorCode":null,"errorMessage":"MultiMC installation draft is not open","messagePattern":"MultiMC installation draft is not open","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java","lineNumber":450,"sourceCode":"    ///\n    /// @return the primary JAR destination\n    private Path getPrimaryJarFile() {\n        if (updateTarget == null) {\n            return repository.getLayout().getInstanceJarFile(instanceId);\n        }\n\n        Path manifestFile = updateTarget.getManifestFile();\n        return manifestFile.resolveSibling(FileUtils.getNameWithoutExtension(manifestFile) + \".jar\");\n    }\n\n    /// Returns the open draft associated with this task.\n    ///\n    /// @return the open draft\n    /// @throws IllegalStateException if the task has not reserved a draft or already released it\n    private DefaultGameRepositoryDraft requireDraft() {\n        @Nullable DefaultGameRepositoryDraft currentDraft = draft;\n        if (currentDraft == null || !currentDraft.isOpen()) {\n            throw new IllegalStateException(\"MultiMC installation draft is not open\");\n        }\n        return currentDraft;\n    }\n\n    /// Aborts the task's draft when execution ends before commit.\n    private void abortOpenDraft() {\n        @Nullable DefaultGameRepositoryDraft currentDraft = draft;\n        if (currentDraft == null || !currentDraft.isOpen()) {\n            return;\n        }\n        try {\n            currentDraft.abort();\n        } catch (IOException e) {\n            LOG.warning(\"Failed to abort MultiMC installation draft for \" + instanceId, e);\n        }\n    }\n\n    private FileSystem openModpack() throws IOException {","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java#L432-L468","documentation":"Internal invariant check: requireDraft() returns the repository draft opened in preExecute, which is closed (aborted) automatically when the task ends unsuccessfully. Calling execute/postExecute logic that needs the draft after it was released means the task is being driven out of order or already failed.","triggerScenarios":"requireDraft() invoked from execute() or postExecute() when draft is null (preExecute failed before reserving it) or the draft was already aborted/committed.","commonSituations":"Running postExecute after a dependency failure that triggered abortOpenDraft; externally re-running a finished or failed Task object.","solutions":["Do not reuse the task; create a new MultiMCModpackInstallTask instance and run it once.","Ensure preExecute and dependencies succeeded before postExecute uses the draft.","Inspect why the task was aborted earlier (check logs for the original failure)."],"exampleFix":"// before\ntask.run();\ntask.postExecute(); // draft already aborted\n// after\nif (task.isDependenciesSucceeded()) {\n    // postExecute is invoked by the task framework itself; never call it manually\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { task.run(); } catch (IllegalStateException e) { if (e.getMessage().contains(\"draft is not open\")) { /* recreate and rerun the task */ } }","preventionTips":["Run each Task instance exactly once via the task framework","Never invoke execute/postExecute manually","Create a new task after any failure"],"tags":["java","task-lifecycle","internal","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}