aeron-io/aeron · error · ArchiveException
invalid nextRecordingId: expected value greater or equal to…
Error message
invalid nextRecordingId: expected value greater or equal to " + (recordingId + 1) + ", was " + nextRecordingId
What it means
Thrown when the catalog's nextRecordingId field, persisted in the catalog file, is lower than recordingId + 1 for an existing entry — i.e. the catalog's high-water-mark of recording IDs is inconsistent with an entry it contains. This guards against corrupted or hand-edited catalog files where the next ID counter was reset while entries still exist, which would cause new recordings to reuse existing IDs. Only thrown when the catalog is writable.
Solutions
- Restore a consistent catalog: run ArchiveTool verify/recover against the archive directory to rebuild or repair the catalog
- Restore the correct catalog file from a backup that includes all recorded entries
- Repair nextRecordingId to recordingId + 1 using CatalogTool or ArchiveTool repair options if you accept the risk after verifying entries
- Open the catalog read-only (writable=false) if you only need to inspect recordings, which bypasses this check
Example fix
// before cp backup/catalog catalog // older catalog overwritten newer one // after java -cp aeron-samples.jar io.aeron.archive.tool.ArchiveTool verify /archive/dir # then restore catalog from a backup taken after the last recording, or repair nextRecordingId
Defensive patterns
Strategy: try-catch
Validate before calling
// Before opening writable, sanity check the catalog header via CatalogTool: // java -cp aeron-samples.jar io.aeron.archive.tool.CatalogTool describe <archive-dir>
Try / catch
try (Catalog catalog = new Catalog(archiveDir, clock, fileSyncLevel, writable=true, capacity, null, null)) {
// use catalog
} catch (ArchiveException e) {
if (e.getMessage().startsWith("invalid nextRecordingId")) {
// catalog header inconsistent with entries: run ArchiveTool verify / restore backup
} else {
throw e;
}
} Prevention
- Shut down the archive and media driver gracefully; avoid kill -9 during recording
- Never manually edit or truncate catalog files
- Take catalog backups only after clean shutdowns
- Run ArchiveTool verify after any crash before restarting the archive
When it happens
Trigger: Opening a writable Catalog whose header nextRecordingId is 0-positive but < max(recordingId)+1, typically after a corrupted catalog, a partial write/crash during catalog update, manual truncation, or copying an older catalog file over a newer one.
Common situations: Disk corruption or crash without clean shutdown truncating catalog updates; restoring catalog from backup taken before newer recordings were made; mixing catalog files across archive instances; ArchiveTool usage that mutated entries without updating the header counter.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- catalogFileSyncLevel
- invalid fileIoMaxLength=
- Archive.Context.controlChannel must be set
- Archive.Context.controlChannel must be UDP media: uri=
- local control channel must be IPC media: uri=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/b3a5f9ebcaa5956a.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/Catalog.java:976
recordingId = recordingId(catalogBuffer);
if (isValidDescriptor(catalogBuffer))
{
catalogIndex.add(recordingId, offset);
}
offset += frameLength;
}
nextRecordingDescriptorOffset = offset;
if (0 == nextRecordingId)
{
nextRecordingId = recordingId + 1;
}
else if (writable && nextRecordingId < recordingId + 1)
{
throw new ArchiveException("invalid nextRecordingId: expected value greater or equal to " +
(recordingId + 1) + ", was " + nextRecordingId);
}
}
private void invokeEntryProcessor(final int recordingDescriptorOffset, final CatalogEntryProcessor consumer)
{
descriptorHeaderDecoder.wrap(
catalogBuffer, 0, DESCRIPTOR_HEADER_LENGTH, RecordingDescriptorHeaderDecoder.SCHEMA_VERSION);
descriptorHeaderEncoder.wrap(catalogBuffer, 0);
descriptorDecoder.wrap(
catalogBuffer,
DESCRIPTOR_HEADER_LENGTH,
RecordingDescriptorDecoder.BLOCK_LENGTH,
RecordingDescriptorDecoder.SCHEMA_VERSION);
descriptorEncoder.wrap(catalogBuffer, DESCRIPTOR_HEADER_LENGTH);View on GitHub (pinned to 6d60124e15)