apache/cassandra · error · RuntimeException

Can't open incompatible SSTable! Current version

Error message

Can't open incompatible SSTable! Current version %s, found file: %s

What it means

moveAndOpenSSTable() refuses to move/open an SSTable whose descriptor version is not compatible with the currently supported format. It throws a RuntimeException naming the latest supported version and the offending file, preventing opening of SSTables written by incompatible Cassandra versions or formats.

Solutions

  1. Run nodetool upgradesstables (or sstableupgrade) on the data before moving/opening
  2. Load old SSTables through sstableloader, which rewrites them to the current version
  3. If the file truly is incompatible/unreadable, remove it and rebuild data via nodetool repair from other replicas
  4. Verify the intended version with sstablemetadata and confirm the Cassandra version matches the data files

Example fix

// before: moving and opening old-version sstable directly
SSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false);
// after: upgrade first
// run: nodetool upgradesstables --ks ks --cf tbl
SSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false);
Defensive patterns

Strategy: validation

Validate before calling

// check descriptor compatibility before moving
Descriptor d = Descriptor.fromFilename(file);
if (!d.isCompatible()) { upgradeFirst(); } // e.g. run sstableupgrade

Try / catch

try { SSTableReader.moveAndOpenSSTable(cfs, oldDesc, newDesc, comps, false); }
catch (RuntimeException e) {
    if (e.getMessage().startsWith("Can't open incompatible SSTable")) {
        logger.error("SSTable version incompatible: {}", oldDesc, e);
        runUpgradesstables();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling moveAndOpenSSTable (e.g. during sstable upgrade/relocation or offline tools) with oldDescriptor whose version fails isCompatible() — typically SSTables from an older major version no longer supported, or foreign/exotic formats.

Common situations: Upgrading from a very old Cassandra release without running upgradesstables first; restoring backups written by an older major version; point tools at mixed-version data directories.

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 apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/411bc9fe13791764. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java:1869

        @Override
        public final boolean equals(Object o)
        {
            if (!(o instanceof IndexesBounds))
                return false;
            IndexesBounds that = (IndexesBounds) o;
            return lowerPosition == that.lowerPosition && upperPosition == that.upperPosition;
        }
    }

    /**
     * Moves the sstable in oldDescriptor to a new place (with generation etc) in newDescriptor.
     * <p>
     * All components given will be moved/renamed
     */
    public static SSTableReader moveAndOpenSSTable(ColumnFamilyStore cfs, Descriptor oldDescriptor, Descriptor newDescriptor, Set<Component> components, boolean copyData)
    {
        if (!oldDescriptor.isCompatible())
            throw new RuntimeException(String.format("Can't open incompatible SSTable! Current version %s, found file: %s",
                                                     oldDescriptor.getFormat().getLatestVersion(),
                                                     oldDescriptor));

        boolean isLive = cfs.getLiveSSTables().stream().anyMatch(r -> r.descriptor.equals(newDescriptor)
                                                                      || r.descriptor.equals(oldDescriptor));
        if (isLive)
        {
            String message = String.format("Can't move and open a file that is already in use in the table %s -> %s", oldDescriptor, newDescriptor);
            logger.error(message);
            throw new RuntimeException(message);
        }
        if (newDescriptor.fileFor(Components.DATA).exists())
        {
            String msg = String.format("File %s already exists, can't move the file there", newDescriptor.fileFor(Components.DATA));
            logger.error(msg);
            throw new RuntimeException(msg);
        }

View on GitHub (pinned to 88fd0f6a0e)