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

  1. Use the correct, role-specific mark file: clustered services derive their filename from the serviceId (see ClusterMarkFile filename helper); ensure serviceId differs per role.
  2. Point each component at its own cluster directory or remove/replace the incompatible mark file if the data is stale.
  3. 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

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


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)