aeron-io/aeron · error · ArchiveException

segment file already exists: " + file

Error message

segment file already exists: " + file

What it means

RecordingWriter throws this when rolling over to a new recording segment file and the target segment file already exists in the archive directory. Aeron archive segments are addressed deterministically by recordingId + segmentBasePosition, so a pre-existing file means recorded data would be silently overwritten or the recording log would be inconsistent, so the writer fails fast instead.

Solutions

  1. Delete or move the conflicting segment file from archiveDir, or choose a fresh archive directory for the new recording
  2. If the old recording is unwanted, remove all files for that recordingId (segment files named <recordingId>-<position>.rec) so rollover targets are free
  3. If the old recording must be kept, let the catalog assign a new recordingId instead of forcing/reusing one
  4. Check for a second archive process or stale recording session writing to the same directory and stop it before resuming

Example fix

// before: blind reuse of a dirty archive dir
archive-media-dir=/data/archive

// after: assert a clean target or clean it before starting the archive/recording
Files.move(Paths.get("/data/archive", recordingId + "-*.rec"), backupDir); // or delete if stale
Defensive patterns

Strategy: validation

Validate before calling

File archiveDir = new File(cfg.archiveDir);
for (long recId : recordingIdsToCreate) {
    File[] existing = archiveDir.listFiles((d, n) -> n.startsWith(recId + "-"));
    if (existing != null && existing.length > 0) {
        throw new IllegalStateException("stale segments for recordingId " + recId);
    }
}

Type guard

static boolean segmentFileOccupied(File archiveDir, long recordingId, long position) {
    return new File(archiveDir, Archive.segmentFileName(recordingId, position)).exists();
}

Try / catch

try {
    archive.startRecording(channel, streamId, sourceLocation);
} catch (ArchiveException e) {
    if (e.getMessage().contains("segment file already exists")) {
        cleanOrRotateArchiveDir(); // remove stale segments, then retry
    } else { throw e; }
}

Prevention

When it happens

Trigger: onFileRollOver (invoked from onBlock when the segment length is exceeded) computes Archive.segmentFileName(recordingId, segmentBasePosition) and finds the file already on disk. Typical causes: re-recording onto a recordingId/directory that already contains segments, leftover segments from a crashed or aborted recording, or a misconfigured archiveDir pointing at a reused/non-empty directory with recordingId collisions.

Common situations: Restarting a recording after a crash without cleaning the archive dir; archiving into a shared or restored-from-backup directory where old segment files persist; replaying/recording fixtures that recreate the same recordingId and start position; mounting the same archiveDir for two archive processes.

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 aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/9d7a55a6c45e7f1d. Report an issue: GitHub.

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/RecordingWriter.java:243

        }
        catch (final IOException ex)
        {
            CloseHelper.close(recordingFile);
            close();
            LangUtil.rethrowUnchecked(ex);
        }
    }

    private void onFileRollOver()
    {
        CloseHelper.close(recordingFileChannel);
        segmentOffset = 0;
        segmentBasePosition += segmentLength;

        final File file = new File(archiveDir, Archive.segmentFileName(recordingId, segmentBasePosition));
        if (file.exists())
        {
            throw new ArchiveException("segment file already exists: " + file);
        }

        openRecordingSegmentFile(file);
    }

    private void checkErrorType(final IOException ex, final int writeLength)
    {
        boolean isLowStorageSpace = false;
        IOException suppressed = null;

        try
        {
            isLowStorageSpace = StorageSpaceException.isStorageSpaceError(ex) ||
                ctx.archiveFileStore().getUsableSpace() < writeLength;
        }
        catch (final IOException ex2)
        {
            suppressed = ex2;

View on GitHub (pinned to 6d60124e15)