aeron-io/aeron · error · ArchiveException
Unknown session or token timeout for replayToken=" +…
Error message
Unknown session or token timeout for replayToken=" + replayToken
What it means
When a connect request arrives on a MDC control-mode=response channel with a replayToken, the adapter looks up the corresponding replay session in the archive conductor via getReplaySession(replayToken, recordingId). If no session is registered for that token (unknown token or the token timed out), the connection cannot be correlated and an ArchiveException is thrown.
Solutions
- Use the replay token promptly after requesting the replay; if it expired, issue a new startReplay call to obtain a fresh token.
- Ensure the connect goes to the same archive instance that issued the token (no cross-node routing unless state is shared).
- Verify the recordingId paired with the token matches the replay session that was started.
- Check archive conductor logs for session expiry to confirm a timeout versus an unknown token.
Example fix
// before long token = staleTokenFromPreviousRun; archive.connect(uri, token); // after long token = archive.startReplay(recordingId, position, length, replayChannel, replayStreamId); archive.connect(uri, token); // use immediately
Defensive patterns
Strategy: retry
Validate before calling
// use the token immediately after obtaining it
long token = archive.startReplay(recId, pos, len, ch, streamId);
if (token == Aeron.NULL_VALUE) throw new IllegalStateException("no token issued"); Try / catch
try {
controlResponsePoller.resubmitResponseChannelSetup(uri, token);
} catch (ArchiveException e) {
if (e.getMessage().startsWith("Unknown session")) {
token = archive.startReplay(recId, pos, len, ch, streamId); // re-issue
}
} Prevention
- Issue and consume replay tokens within the same call flow; never cache across runs.
- Do not route archive connects through load balancers that can switch nodes.
- Confirm the recordingId passed with the token matches the replay session.
When it happens
Trigger: Calling ArchiveClient with a replayToken whose replay session has already ended or was never created on that archive; reusing a token after its timeout; using a token issued by a different archive instance; long delay between token issuance and connect so the conductor's replay session expired.
Common situations: Client retries a connect long after the original replay finished; load balancer routes the connect to a different archive node than the one that issued the token; token typed/copied incorrectly; slow startup so the replay session timed out before connect.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Aeron client instance must set…
- Aeron client must use a RethrowingErrorHandler
- AeronArchive.Context.controlRequestChannel must be set
- AeronArchive.Context.controlResponseChannel must be set
- AeronArchive.Context.messageRetryAttempts must be > 0, got:
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/a7b7271e4c97d6c5.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ControlSessionAdapter.java:1180
}
}
private ControlSession setupSessionAndChannelForReplay(
final ChannelUri channelUri,
final long replayToken,
final long recordingId,
final long correlationId,
final long controlSessionId,
final int templateId,
final Image image)
{
final ControlSession controlSession;
if (channelUri.hasControlModeResponse() && Aeron.NULL_VALUE != replayToken)
{
controlSession = conductor.getReplaySession(replayToken, recordingId);
if (null == controlSession)
{
throw new ArchiveException("Unknown session or token timeout for replayToken=" + replayToken);
}
channelUri.put(RESPONSE_CORRELATION_ID_PARAM_NAME, Long.toString(image.correlationId()));
}
else
{
controlSession = getControlSession(correlationId, controlSessionId, templateId, image);
}
return controlSession;
}
private ControlSession getControlSession(
final long correlationId, final long controlSessionId, final int templateId, final Image image)
{
final SessionInfo info = controlSessionByIdMap.get(controlSessionId);
if (null != info)
{
if (info.image != image)View on GitHub (pinned to 6d60124e15)