aeron-io/aeron · error · IllegalStateException

ReplayMerge Image closed unexpectedly.

Error message

ReplayMerge Image closed unexpectedly.

What it means

During catchup, ReplayMerge polls the replay image; if no new position progress is recorded in a poll and the image is closed, the library concludes the replay ended abnormally (rather than reaching the merge position) and throws IllegalStateException 'ReplayMerge Image closed unexpectedly.'

Solutions

  1. Check archive logs for the replay session termination reason
  2. Retry the ReplayMerge from a new recording position once the archive is available
  3. Verify network stability to the replay destination
  4. Handle the exception and fall back to a fresh ArchiveClient.replay or full subscription

Example fix

// before: no recovery
merge.doWork(); // throws, aborts
// after
try { merge.doWork(); } catch (IllegalStateException e) { if (e.getMessage().contains("Image closed unexpectedly")) { reinitReplayMerge(); } }
Defensive patterns

Strategy: try-catch

Validate before calling

if (image.isClosed()) { reinitializeReplayMerge(); } // check before polling each cycle

Try / catch

try { merge.doWork(); }
catch (IllegalStateException e) { if (e.getMessage().contains("Image closed unexpectedly")) { recreateReplayMerge(recordingId); } }

Prevention

When it happens

Trigger: The replay image is closed before the catchup position is reached — archive session terminated, archive connection dropped, or the replay was cancelled — while ReplayMerge.doWork runs catchup.

Common situations: Archive process restart or failover mid-catchup; network disruption between client and archive replay destination; recording ends/deleted causing session termination.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/f8a1bbe419270d18. Report an issue: GitHub.

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/ReplayMerge.java:465

        if (null != image)
        {
            final long position = image.position();
            if (position >= nextTargetPosition)
            {
                timeOfLastProgressMs = nowMs;
                positionOfLastProgress = position;
                state(State.ATTEMPT_LIVE_JOIN);
                workCount += 1;
            }
            else if (position > positionOfLastProgress)
            {
                timeOfLastProgressMs = nowMs;
                positionOfLastProgress = position;
            }
            else if (image.isClosed())
            {
                throw new IllegalStateException("ReplayMerge Image closed unexpectedly.");
            }
        }

        return workCount;
    }

    private int attemptLiveJoin(final long nowMs)
    {
        int workCount = 0;

        if (NULL_VALUE == activeCorrelationId)
        {
            if (callGetMaxRecordedPosition(nowMs))
            {
                timeOfLastProgressMs = nowMs;
                workCount += 1;
            }
        }

View on GitHub (pinned to 6d60124e15)