aeron-io/aeron · error · ArchiveEvent
ACTIVE_RECORDING
ACTIVE_RECORDING
Error message
cannot extend active recording ${recordingId} streamId=${streamId} channel=${originalChannel} What it means
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.
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.
Example fix
// before long recId = archive.startRecording(channel, streamId, SourceLocation.LOCAL); archive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL); // still active! // after long recId = archive.startRecording(channel, streamId, SourceLocation.LOCAL); archive.stopRecording(subscription); awaitRecordingStopped(archive, recId); // wait for RecordingSignal.STOP archive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL);
Defensive patterns
Strategy: validation
Validate before calling
// client-side pre-check via ListRecordingsForUri + recording signals
RecordingSignal last = awaitSignal(archive, recordingId);
if (last != RecordingSignal.STOP) {
throw new IllegalStateException("recording " + recordingId + " still active; cannot extend");
} Try / catch
try {
archive.extendRecording(recId, channel, streamId, SourceLocation.LOCAL);
} catch (ArchiveException e) {
if (e.errorCode() == ArchiveErrorCode.ACTIVE_RECORDING) {
// wait for stop, then retry extend
}
} Prevention
- 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.
When it happens
Trigger: Client calls AeronArchive.extendRecording()/startRecording with a replay/extend subscription whose recordingId still has a live RecordingSession in recordingSessionByIdMap (recording not stopped).
Common situations: 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).
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
- GENERIC
- unknown recording id: " + recordingId
- catalogFileSyncLevel
- invalid fileIoMaxLength=
- Archive.Context.controlChannel must be set
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/4fe948d11f17bcc3.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java:2076
private void extendRecordingSession(
final ControlSession controlSession,
final long correlationId,
final long recordingId,
final String strippedChannel,
final String originalChannel,
final Image image,
final boolean autoStop)
{
final long subscriptionId = image.subscription().registrationId();
try
{
if (recordingSessionByIdMap.containsKey(recordingId))
{
final String msg = "cannot extend active recording " + recordingId +
" streamId=" + image.subscription().streamId() + " channel=" + originalChannel;
controlSession.sendErrorResponse(correlationId, ACTIVE_RECORDING, msg);
throw new ArchiveEvent(msg);
}
catalog.recordingSummary(recordingId, recordingSummary);
final DeleteSegmentsSession deleteSegmentsSession = deleteSegmentsSessionByIdMap.get(recordingId);
if (null != deleteSegmentsSession &&
deleteSegmentsSession.maxDeletePosition() >= recordingSummary.stopPosition)
{
final String msg = "cannot extend recording " + recordingId +
" due to an outstanding delete operation: streamId=" +
image.subscription().streamId() + " channel=" + originalChannel;
controlSession.sendErrorResponse(correlationId, GENERIC, msg);
throw new ArchiveEvent(msg);
}
validateImageForExtendRecording(correlationId, controlSession, image, recordingSummary);
final Counter position = RecordingPos.allocate(View on GitHub (pinned to 6d60124e15)