HMCL-dev/HMCL · error · IOException
Too many attempts
Error message
Too many attempts
What it means
WorldBackupTask.execute tries to create a zip file for a world backup, repeatedly attempting an alternate filename when FileAlreadyExistsException occurs. After exhausting its attempts (the retry loop falls through), outputStream remains null and the task throws IOException("Too many attempts") instead of silently failing. It signals that no free backup filename could be found.
Solutions
- Delete or archive old world backup zips from the backups directory so a free filename is available
- Point the backup output to a different/empty directory
- Check the backup filename generation (timestamp/counter) for a bug that yields the same name every attempt
- Verify write permissions on the backups directory (a persistent failure mode can mask free names)
Example fix
// before
if (outputStream == null)
throw new IOException("Too many attempts");
// after
if (outputStream == null)
throw new IOException("Too many attempts: could not find a free backup file name in "
+ backupDir + "; please clean up old backups"); Defensive patterns
Strategy: retry
Validate before calling
// before backing up
long existing = Files.list(backupDir)
.filter(p -> p.getFileName().toString().startsWith(worldName))
.count();
if (existing > MAX_ATTEMPTS - 2) {
Files.move(oldestBackup, archiveDir.resolve(oldestBackup.getFileName()));
} Try / catch
try {
task.execute();
} catch (IOException e) {
if (e.getMessage().startsWith("Too many attempts")) {
cleanOldBackups();
retryBackup();
} else throw e;
} Prevention
- Schedule periodic cleanup/pruning of old world backups
- Use timestamped filenames with millisecond precision to avoid collisions
- Monitor backup directory size and alert before it fills with stale zips
When it happens
Trigger: Running a world backup when every candidate output zip filename already exists in the backups directory, so the create-new-file attempt keeps throwing FileAlreadyExistsException until the loop's attempt count is exhausted.
Common situations: Backing up a world repeatedly without deleting old backups so the counter-space filenames collide; a backup directory with many stale/partial zip files; clock or counter logic producing the same name each run; read-only or synced (e.g. cloud-synced) backup folders where files reappear.
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
- name existing
- Failed to close session lock channel of the world
- "Mod " + modFile + " `mcmod.info` is malformed"
- "File " + modFile + " is not a LiteLoader mod."
- "File " + modFile + " is not a Quilt mod."
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/4a9a842fa7a49f90.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupTask.java:68
try (FileChannel lockChannel = needLock ? world.lock() : null) {
Files.createDirectories(backupsDir);
String time = LocalDateTime.now().format(WorldBackupsPage.TIME_FORMATTER);
String baseName = time + "_" + world.getFileName();
Path backupFile = null;
OutputStream outputStream = null;
int count;
for (count = 0; count < 256; count++) {
try {
backupFile = backupsDir.resolve(baseName + (count == 0 ? "" : " " + count) + ".zip").toAbsolutePath();
outputStream = Files.newOutputStream(backupFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
break;
} catch (FileAlreadyExistsException ignored) {
}
}
if (outputStream == null)
throw new IOException("Too many attempts");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
String rootName = world.getFileName();
Path rootDir = this.world.getFile();
Files.walkFileTree(this.world.getFile(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (path.endsWith("session.lock")) {
return FileVisitResult.CONTINUE;
}
zipOutputStream.putNextEntry(new ZipEntry(rootName + "/" + rootDir.relativize(path).toString().replace('\\', '/')));
Files.copy(path, zipOutputStream);
zipOutputStream.closeEntry();
return FileVisitResult.CONTINUE;
}
});
}
View on GitHub (pinned to 24702dc5a0)