HMCL-dev/HMCL · error · IllegalStateException

MultiMC installation draft is not open

Error message

MultiMC installation draft is not open

What it means

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.

Solutions

  1. Do not reuse the task; create a new MultiMCModpackInstallTask instance and run it once.
  2. Ensure preExecute and dependencies succeeded before postExecute uses the draft.
  3. Inspect why the task was aborted earlier (check logs for the original failure).

Example fix

// before
task.run();
task.postExecute(); // draft already aborted
// after
if (task.isDependenciesSucceeded()) {
    // postExecute is invoked by the task framework itself; never call it manually
}
Defensive patterns

Strategy: try-catch

Try / catch

try { task.run(); } catch (IllegalStateException e) { if (e.getMessage().contains("draft is not open")) { /* recreate and rerun the task */ } }

Prevention

When it happens

Trigger: requireDraft() invoked from execute() or postExecute() when draft is null (preExecute failed before reserving it) or the draft was already aborted/committed.

Common situations: Running postExecute after a dependency failure that triggered abortOpenDraft; externally re-running a finished or failed Task object.

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


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/cd2d0e0d5163be67. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java:450

    ///
    /// @return the primary JAR destination
    private Path getPrimaryJarFile() {
        if (updateTarget == null) {
            return repository.getLayout().getInstanceJarFile(instanceId);
        }

        Path manifestFile = updateTarget.getManifestFile();
        return manifestFile.resolveSibling(FileUtils.getNameWithoutExtension(manifestFile) + ".jar");
    }

    /// Returns the open draft associated with this task.
    ///
    /// @return the open draft
    /// @throws IllegalStateException if the task has not reserved a draft or already released it
    private DefaultGameRepositoryDraft requireDraft() {
        @Nullable DefaultGameRepositoryDraft currentDraft = draft;
        if (currentDraft == null || !currentDraft.isOpen()) {
            throw new IllegalStateException("MultiMC installation draft is not open");
        }
        return currentDraft;
    }

    /// Aborts the task's draft when execution ends before commit.
    private void abortOpenDraft() {
        @Nullable DefaultGameRepositoryDraft currentDraft = draft;
        if (currentDraft == null || !currentDraft.isOpen()) {
            return;
        }
        try {
            currentDraft.abort();
        } catch (IOException e) {
            LOG.warning("Failed to abort MultiMC installation draft for " + instanceId, e);
        }
    }

    private FileSystem openModpack() throws IOException {

View on GitHub (pinned to 24702dc5a0)