{"record":{"id":"e59b068863686da7","repo":"aeron-io/aeron","slug":"invalid-extension","errorCode":"INVALID_EXTENSION","errorMessage":"cannot extend recording ${recordingId} image.joinPosition=${joinPosition} != rec.stopPosition=${stopPosition}","messagePattern":"cannot extend recording (.+?) image\\.joinPosition=(.+?) != rec\\.stopPosition=(.+?)","errorType":"error_code","errorClass":"ArchiveEvent","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java","lineNumber":2166,"sourceCode":"                return subscription;\n            }\n        }\n\n        return null;\n    }\n\n    private void validateImageForExtendRecording(\n        final long correlationId,\n        final ControlSession controlSession,\n        final Image image,\n        final RecordingSummary recordingSummary)\n    {\n        if (image.joinPosition() != recordingSummary.stopPosition)\n        {\n            final String msg = \"cannot extend recording \" + recordingSummary.recordingId +\n                \" image.joinPosition=\" + image.joinPosition() + \" != rec.stopPosition=\" + recordingSummary.stopPosition;\n            controlSession.sendErrorResponse(correlationId, INVALID_EXTENSION, msg);\n            throw new ArchiveEvent(msg);\n        }\n\n        if (image.initialTermId() != recordingSummary.initialTermId)\n        {\n            final String msg = \"cannot extend recording \" + recordingSummary.recordingId +\n                \" image.initialTermId=\" + image.initialTermId() +\n                \" != rec.initialTermId=\" + recordingSummary.initialTermId;\n            controlSession.sendErrorResponse(correlationId, INVALID_EXTENSION, msg);\n            throw new ArchiveEvent(msg);\n        }\n\n        if (image.termBufferLength() != recordingSummary.termBufferLength)\n        {\n            final String msg = \"cannot extend recording \" + recordingSummary.recordingId +\n                \" image.termBufferLength=\" + image.termBufferLength() +\n                \" != rec.termBufferLength=\" + recordingSummary.termBufferLength;\n            controlSession.sendErrorResponse(correlationId, INVALID_EXTENSION, msg);\n            throw new ArchiveEvent(msg);","sourceCodeStart":2148,"sourceCodeEnd":2184,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java#L2148-L2184","documentation":"Thrown by ArchiveConductor when a client requests extending an existing recording with a new publication whose Image joinPosition does not equal the recording's stored stopPosition. A valid extension must continue the recording exactly where it stopped, so any position gap or overlap is rejected with error code INVALID_EXTENSION. The archive guarantees that extended recordings remain a contiguous, gap-free stream.","triggerScenarios":"Calling AeronArchive.extendRecording (or extendRecordingChannel) with a publication whose Image joinPosition differs from the original recording's stopPosition, e.g. the original recording stopped mid-stream or the extending publication started at a different position.","commonSituations":"Extending a recording with a publication from a different source/stream than the original; the original recording was stopped and restarted by a separate publication that did not carry over the position; replay/extension across cluster snapshots or term-id drift; passing the wrong recordingId to extendRecording.","solutions":["Ensure the publication used to extend the recording joins at exactly the recorded stopPosition (same source, same stream, position continuity preserved).","Verify you passed the correct recordingId and that no other publication has advanced the recording since it stopped.","Re-record from the beginning instead of extending if position continuity cannot be guaranteed.","Check that the extension publication is on the same channel/streamId as the original recording."],"exampleFix":"// before\narchive.extendRecording(recordingId, false, channel, streamId); // publication started fresh at position 0\n// after\nlong expected = archive.listRecording(recordingId).stopPosition;\n// only extend with an original publication whose joinPosition == expected stopPosition,\n// otherwise start a new recording instead of extending","handlingStrategy":"validation","validationCode":"RecordingSubscriptionDescriptor rec = archive.listRecording(recordingId);\nImage image = /* subscription image for the extending publication */;\nif (rec == null || image.joinPosition() != rec.stopPosition) {\n    throw new IllegalStateException(\"joinPosition \" + image.joinPosition() +\n        \" != stopPosition \" + (rec == null ? \"<missing>\" : rec.stopPosition));\n}","typeGuard":null,"tryCatchPattern":"try {\n    archive.extendRecording(recordingId, isOriginal, channel, streamId);\n} catch (ArchiveException e) {\n    if (e.errorCode() == ArchiveException.INVALID_EXTENSION) {\n        // fall back to starting a new recording\n        archive.startRecording(channel, streamId, isOriginal);\n    }\n}","preventionTips":["Always extend with the original publication that produced the recording.","Query stopPosition via listRecording before extending and assert equality with image.joinPosition().","Do not restart recordings with brand-new publications if you plan to extend later; use replay-extend for continuing streams."],"tags":["aeron-archive","recording-extension","position-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}