aeron-io/aeron · error · ClusterException
initial ack already received from service: possible…
Error message
initial ack already received from service: possible duplicate serviceId=<serviceId>
What it means
captureServiceAck records the first (ackId==0) ack from each clustered service, keyed by serviceId. If a second initial ack arrives for a service that already recorded one, the internal invariant (exactly one initial ack per service) is violated, so ClusterException is thrown.
Solutions
- Ensure each ClusteredService in the configuration has a unique serviceId
- Check that service containers are not started twice against the same cluster
- Review custom ClusteredService implementations for duplicate ack(AckId 0) sends
- Restart the node cleanly if a partially-restarted container left stale ack state
Example fix
// before: duplicate serviceId across services ClusteredServiceContainer.Configuration.serviceId(0); // service A ClusteredServiceContainer.Configuration.serviceId(0); // service B // after: unique ids ClusteredServiceContainer.Configuration.serviceId(0); // service A ClusteredServiceContainer.Configuration.serviceId(1); // service B
Defensive patterns
Strategy: validation
Validate before calling
// validate unique serviceIds at startup
Set<Integer> ids = new HashSet<>();
for (ClusteredServiceContainer.Context c : serviceContexts)
{
if (!ids.add(c.serviceId()))
{
throw new IllegalArgumentException("duplicate serviceId: " + c.serviceId());
}
} Try / catch
catch (ClusterException e)
{
if (e.getMessage().contains("duplicate serviceId"))
{
log.error("Configuration error: two services share one serviceId", e);
}
throw e;
} Prevention
- Assign each ClusteredService a distinct serviceId in configuration
- Never start the same service container twice against one cluster
- Audit custom services for duplicate initial acks
When it happens
Trigger: Thrown when ackId is 0 and serviceClientIds[serviceId] is not NULL_VALUE, i.e. a duplicate ackId=0 ack from the same serviceId.
Common situations: Two services configured with the same serviceId; a service container restarted and re-sent its initial ack; a misbehaving or buggy custom ClusteredService sending duplicate acks.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- response publication already added
- failed to start clustered service(s)
- not found in
- ${eventCode}: ${detail}
- clusterMembers and endpoints differ
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/4fadf0607b7107cf.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModuleAgent.java:2720
NodeControl.ToggleState.reset(nodeControlToggle);
return 1;
}
return 0;
}
private boolean appendAction(final ClusterAction action, final long timestamp, final int flags)
{
return logPublisher.appendClusterAction(leadershipTermId, timestamp, action, flags);
}
private void captureServiceAck(final long logPosition, final long ackId, final long relevantId, final int serviceId)
{
if (0 == ackId && NULL_VALUE != serviceClientIds[serviceId])
{
throw new ClusterException(
"initial ack already received from service: possible duplicate serviceId=" + serviceId);
}
serviceAckQueues[serviceId].offerLast(new ServiceAck(ackId, logPosition, relevantId));
}
private boolean tryCreateAppendPosition(final int logSessionId)
{
final CountersReader counters = aeron.countersReader();
final int counterId = RecordingPos.findCounterIdBySession(counters, logSessionId, archive.archiveId());
if (CountersReader.NULL_COUNTER_ID == counterId)
{
return false;
}
final long registrationId = counters.getCounterRegistrationId(counterId);
if (0 == registrationId)
{View on GitHub (pinned to 6d60124e15)