HMCL-dev/HMCL · error · FileAlreadyExistsException
An unregistered instance directory already exists
Error message
An unregistered instance directory already exists
What it means
putManifest() validates new instance ids before accepting them: if the id is not in the base snapshot and not already created by this draft, it is treated as a brand-new instance and its directory must not exist on disk. If a directory is already present at the instance root, it throws FileAlreadyExistsException with the message 'An unregistered instance directory already exists' — HMCL will not silently adopt or overwrite an unregistered folder.
Solutions
- Remove or rename the existing unregistered directory at the instance root before calling put
- Pick a different, non-colliding instance id for the new instance
- If the existing folder is a valid instance you want to keep, load/import it through the repository instead of creating a new one with the same id
- Catch FileAlreadyExistsException, inspect the path in the message, and either clean it up or surface a 'name already in use' prompt to the user
Example fix
// before
try (Draft d = repo.openDraft()) {
d.put(id, manifest); // FileAlreadyExistsException if dir exists
}
// after
Path root = repo.getLayout().getInstanceRoot(id);
if (Files.exists(root)) {
throw new IllegalArgumentException("Instance name in use, pick another: " + id);
}
try (Draft d = repo.openDraft()) {
d.put(id, manifest);
} Defensive patterns
Strategy: validation
Validate before calling
Path root = layout.getInstanceRoot(id).toAbsolutePath().normalize();
if (Files.exists(root)) {
throw new IllegalArgumentException("Directory already exists for id: " + id);
} Try / catch
try {
draft.put(id, manifest);
} catch (FileAlreadyExistsException e) {
LOG.warning("Unregistered directory in the way: " + e.getFile());
// ask user to clean up or choose another id
} Prevention
- Clean up directories left by failed creations
- Avoid reusing ids of previously deleted instances without checking the disk
- Import existing folders through the repository instead of re-creating over them
When it happens
Trigger: Calling draft.put(...) (or putManifest via rename) with an id whose instance directory already exists on disk but which is not registered in the repository — the folder exists but has no known manifest.
Common situations: Creating an instance with the same name as a previously deleted (but not fully removed) instance; users placing raw version folders into the instances directory manually; a prior failed create that left the directory behind; case-insensitive filesystems making ids appear distinct but collide on disk.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/ef253e5f0f61813c.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepositoryDraft.java:222
/// Updates one manifest in the in-memory write set.
///
/// @param manifest the manifest to retain
/// @param claimNewRoot whether a previously absent instance root should become draft-owned
/// @throws IOException if a new instance root cannot be reserved
private void putManifest(
GameInstanceManifest manifest,
boolean claimNewRoot) throws IOException {
GameInstanceID id = manifest.id();
if (claimNewRoot
&& !manifests.containsKey(id)
&& baseSnapshot.get(id) == null
&& !createdIds.contains(id)) {
Path root = baseSnapshot.getLayout().getInstanceRoot(id)
.toAbsolutePath()
.normalize();
if (!Files.notExists(root)) {
throw new FileAlreadyExistsException(root.toString(), null,
"An unregistered instance directory already exists");
}
createdIds.add(id);
}
manifests.put(id, manifest);
removedIds.remove(id);
modifiedIds.add(id);
}
/// {@inheritDoc}
@Override
public DefaultGameRepositorySnapshot commit() throws IOException {
checkOpen();
repository.checkActiveDraft(this);
state = GameRepositoryDraft.State.COMMITTING;
List<RenameOperation> appliedRenames = new ArrayList<>();View on GitHub (pinned to 24702dc5a0)