#2100·aeron

ArchiveTool dump aborts on DELETED/INVALID catalog entries (missing the state filter that describe has)

Author: d9zbglobalCreated Jul 24, 2026Updated Aug 18, 2026

Summary

ArchiveTool <archiveDir> dump throws IllegalArgumentException and aborts the entire run as soon as the catalog contains a recording in a non-VALID state (e.g. DELETED after AeronArchive.purgeRecording, or INVALID). describe, describe-all, and verify all tolerate non-valid entries, but dump hands every catalog entry straight to a RecordingReader, including ones whose segment files have already been deleted.

Because purged recordings remain in the catalog as DELETED tombstones until an offline compact, any long-running archive that rotates/purges recordings (e.g. snapshot rotation) accumulates such entries — making dump effectively unusable on it.

Affected versions: reproduced on 1.52.2; the code path is unchanged on master at time of writing.

Steps to reproduce

  1. Record a stream and stop it.
  2. AeronArchive.purgeRecording(recordingId) (marks the catalog entry DELETED and deletes its segments) — or ArchiveTool <dir> mark-invalid <id>.
  3. ArchiveTool <dir> dump.

Actual

Recording 0
  streamId: 100
  stream length: 40296512
[RecordingDescriptorHeader](... state=DELETED ...)
[RecordingDescriptor](... recordingId=0 | startPosition=3523862336 | stopPosition=3564158848 ...)
Exception in thread "main" java.lang.IllegalArgumentException: length must be positive
	at io.aeron.archive.RecordingReader.<init>(RecordingReader.java:86)
	at io.aeron.archive.ArchiveTool.dump(ArchiveTool.java:1207)
	at io.aeron.archive.ArchiveTool.lambda$dump$4(ArchiveTool.java:579)
	at io.aeron.archive.Catalog.invokeEntryProcessor(Catalog.java:995)
	at io.aeron.archive.Catalog.forEach(Catalog.java:511)
	at io.aeron.archive.ArchiveTool.dump(ArchiveTool.java:578)
	at io.aeron.archive.ArchiveTool.main(ArchiveTool.java:143)

The first non-VALID entry aborts the whole dump; valid recordings later in the catalog are never reached.

Expected

dump skips (or notes and continues past) non-VALID recordings the way describe does, so valid recordings are still dumped.

Root cause

dump (ArchiveTool.java:578) iterates catalog.forEach(...) with no state check, whereas describe guards with if (hd.state() == VALID). For a DELETED/INVALID entry the segments are gone, so the per-recording dumpnew RecordingReader(...) throws IllegalArgumentException: length must be positive (RecordingReader.java:86).

Suggested fix

Skip entries whose headerDecoder.state() != VALID in the dump forEach lambda (mirroring describe); optionally add a dump-all variant if dumping invalid entries is ever wanted.