{"record":{"id":"4fe948d11f17bcc3","repo":"aeron-io/aeron","slug":"active-recording","errorCode":"ACTIVE_RECORDING","errorMessage":"cannot extend active recording ${recordingId} streamId=${streamId} channel=${originalChannel}","messagePattern":"cannot extend active recording (.+?) streamId=(.+?) channel=(.+?)","errorType":"error_code","errorClass":"ArchiveEvent","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java","lineNumber":2076,"sourceCode":"\n    private void extendRecordingSession(\n        final ControlSession controlSession,\n        final long correlationId,\n        final long recordingId,\n        final String strippedChannel,\n        final String originalChannel,\n        final Image image,\n        final boolean autoStop)\n    {\n        final long subscriptionId = image.subscription().registrationId();\n        try\n        {\n            if (recordingSessionByIdMap.containsKey(recordingId))\n            {\n                final String msg = \"cannot extend active recording \" + recordingId +\n                    \" streamId=\" + image.subscription().streamId() + \" channel=\" + originalChannel;\n                controlSession.sendErrorResponse(correlationId, ACTIVE_RECORDING, msg);\n                throw new ArchiveEvent(msg);\n            }\n\n            catalog.recordingSummary(recordingId, recordingSummary);\n\n            final DeleteSegmentsSession deleteSegmentsSession = deleteSegmentsSessionByIdMap.get(recordingId);\n            if (null != deleteSegmentsSession &&\n                deleteSegmentsSession.maxDeletePosition() >= recordingSummary.stopPosition)\n            {\n                final String msg = \"cannot extend recording \" + recordingId +\n                    \" due to an outstanding delete operation: streamId=\" +\n                    image.subscription().streamId() + \" channel=\" + originalChannel;\n                controlSession.sendErrorResponse(correlationId, GENERIC, msg);\n                throw new ArchiveEvent(msg);\n            }\n\n            validateImageForExtendRecording(correlationId, controlSession, image, recordingSummary);\n\n            final Counter position = RecordingPos.allocate(","sourceCodeStart":2058,"sourceCodeEnd":2094,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java#L2058-L2094","documentation":"An extendRecording request targeted a recordingId whose recording session is still active in the archive. The archive refuses to extend a recording that has not yet stopped, sends an ACTIVE_RECORDING error to the control session, and throws ArchiveEvent internally. Extension is only valid for completed recordings.","triggerScenarios":"Client calls AeronArchive.extendRecording()/startRecording with a replay/extend subscription whose recordingId still has a live RecordingSession in recordingSessionByIdMap (recording not stopped).","commonSituations":"Application reuses a recordingId that is still being written to; race where the recording stop has not completed before extend is issued; re-running an extend operation on the same recording twice; configuration where the original recording publication is still open (e.g. unclosed publication keeps the recording active).","solutions":["Wait until the original recording has stopped (stopRecording called and session removed) before extending.","Poll the archive catalog / use RecordingSignal events to confirm the recording reached STOPPED state.","Use a new recordingId instead of extending if the old recording is still live.","Close lingering publications associated with the original recording so its session can terminate.","Add correlation-based retry with backoff if extend races with recording stop."],"exampleFix":"// before\nlong recId = archive.startRecording(channel, streamId, SourceLocation.LOCAL);\narchive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL); // still active!\n\n// after\nlong recId = archive.startRecording(channel, streamId, SourceLocation.LOCAL);\narchive.stopRecording(subscription);\nawaitRecordingStopped(archive, recId); // wait for RecordingSignal.STOP\narchive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL);","handlingStrategy":"validation","validationCode":"// client-side pre-check via ListRecordingsForUri + recording signals\nRecordingSignal last = awaitSignal(archive, recordingId);\nif (last != RecordingSignal.STOP) {\n    throw new IllegalStateException(\"recording \" + recordingId + \" still active; cannot extend\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    archive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL);\n} catch (ArchiveException e) {\n    if (e.errorCode() == ArchiveErrorCode.ACTIVE_RECORDING) {\n        // wait for stop, then retry extend\n    }\n}","preventionTips":["Always stopRecording and wait for the STOP recording signal before extending.","Track recording lifecycles via RecordingSignal events.","Never reuse a recordingId while its session may be live.","Close the original publication so recording sessions terminate promptly."],"tags":["aeron-archive","extend-recording","archive-event","conflict"],"backgroundTag":"invalid-state-transition","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}