aeron-io/aeron · critical · ClusterException
existing Mark file type
Error message
existing Mark file type <existingType> not same as required type <type>
What it means
When opening an existing ClusterMarkFile, the stored componentType in the header is compared with the required type (consensus module vs clustered service). A mismatch throws ClusterException, except the permitted case of an existing BACKUP file being reused as a CONSENSUS_MODULE. This guards against sharing one mark file between different cluster roles.
Solutions
- Use the correct, role-specific mark file: clustered services derive their filename from the serviceId (see ClusterMarkFile filename helper); ensure serviceId differs per role.
- Point each component at its own cluster directory or remove/replace the incompatible mark file if the data is stale.
- Verify you are not accidentally launching a consensus module with a service's mark file (BACKUP→CONSENSUS_MODULE is the only allowed reuse).
Example fix
// before ClusterMarkFile markFile = new ClusterMarkFile(clusterDir, "cluster-mark", ...); // same file for service // after ClusterMarkFile markFile = new ClusterMarkFile(clusterDir, ClusterMarkFile.filename(serviceId), ...); // role-specific file
Defensive patterns
Strategy: validation
Validate before calling
// java: check the component type recorded in the file before constructing the real mark file
ClusterMarkFile.HeaderDecoder d = readHeaderDecoder(markFile); // your own SBE read of the file
ClusterComponentType existing = d.componentType();
if (existing != ClusterComponentType.UNKNOWN && existing != requiredType &&
!(existing == ClusterComponentType.BACKUP && requiredType == ClusterComponentType.CONSENSUS_MODULE)) {
throw new IllegalStateException("mark file owned by " + existing + ", need " + requiredType);
} Try / catch
try {
new ClusterMarkFile(file, type, ...);
} catch (ClusterException e) {
if (e.getMessage().contains("not same as required type")) {
// check serviceId/directory configuration before retrying
}
throw e;
} Prevention
- Give every clustered service a distinct serviceId so each gets its own mark file.
- Never copy mark files between consensus module, backup, and service roles.
- Keep one cluster directory per role and document which component owns it.
When it happens
Trigger: Starting a clustered service with a serviceId whose mark file was previously created by a different component type (e.g. a consensus module writing to the same file), or pointing components at the same mark file path.
Common situations: Misconfigured clusterDir/serviceId reuse; copy-pasting mark files between roles; switching a process between consensus-module and service mode in the same directory.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- invalid errorBufferLength=
- className is empty
- failed to create Checksum instance for class: " + className
- mark file major version <SemanticVersion.major(version)>…
- ClusterMarkFile headerLength=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/afb452a24f67195c.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/ClusterMarkFile.java:191
null);
final UnsafeBuffer existingBuffer = existingMarkFile.buffer();
if (0 != currentHeaderOffset)
{
headerDecoder.wrapAndApplyHeader(existingBuffer, 0, messageHeaderDecoder);
}
else
{
headerDecoder.wrap(
existingBuffer, 0, MarkFileHeaderDecoder.BLOCK_LENGTH, MarkFileHeaderDecoder.SCHEMA_VERSION);
}
final ClusterComponentType existingType = headerDecoder.componentType();
if (existingType != ClusterComponentType.UNKNOWN && existingType != type)
{
if (existingType != ClusterComponentType.BACKUP || ClusterComponentType.CONSENSUS_MODULE != type)
{
throw new ClusterException(
"existing Mark file type " + existingType + " not same as required type " + type);
}
}
final int existingErrorBufferLength = headerDecoder.errorBufferLength();
final int headerLength = headerDecoder.headerLength();
final UnsafeBuffer existingErrorBuffer =
new UnsafeBuffer(existingBuffer, headerLength, existingErrorBufferLength);
saveExistingErrors(file, existingErrorBuffer, type, CommonContext.fallbackLogger());
existingErrorBuffer.setMemory(0, existingErrorBufferLength, (byte)0);
candidateTermId = headerDecoder.candidateTermId();
if (0 != currentHeaderOffset)
{
markFile = existingMarkFile;
buffer = existingBuffer;View on GitHub (pinned to 6d60124e15)