apache/cassandra · error · IOException

Seeking to a partition at: ${position} did not land after an

Error message

Seeking to a partition at: ${position} did not land after an end-of-partition marker; found 0x${marker}

What it means

When seeking to a partition start, SSTableCursorReader reads the byte immediately before the target position and requires the exact END_OF_PARTITION marker byte (a stricter test than isEndOfPartition's bit check). Any other byte means the seek position is not a true partition boundary, indicating corrupt bounds or a corrupt data file.

Source

Thrown at src/java/org/apache/cassandra/io/sstable/SSTableCursorReader.java:725

    /**
     * Seeks to the start of a partition. Every partition but the file's first follows an
     * end-of-partition marker, and reading that byte leaves the reader at the partition start.
     */
    private void seekPartition(long position) throws IOException
    {
        if (position == 0)
        {
            if (dataReader.getPosition() != 0)
                dataReader.seek(0);
            return;
        }
        dataReader.seek(position - 1);
        // The exact byte, not isEndOfPartition, which is (b & 1) != 0 and so accepts 128 of the 256
        // values. The writer emits END_OF_PARTITION alone for this marker, so the stronger test
        // costs nothing and rejects a mis-sized bound that happens to land on an odd byte.
        int marker = dataReader.readUnsignedByte();
        if (marker != UnfilteredSerializer.END_OF_PARTITION)
            throw new IOException("Seeking to a partition at: " + position + " did not land after an end-of-partition marker; found 0x"
                                  + Integer.toHexString(marker));
    }

    // struct partition {
    //   struct partition_header header
    //   optional<struct row> row
    //   struct unfiltered unfiltereds[];
    //};
    public int readPartitionHeader(PartitionDescriptor header)
    {
        if (state != PARTITION_START) throw new IllegalStateException();
        resetOnPartitionStart();
        try
        {
            header.load(dataReader, deletionTimeSerializer);
            return checkNextFlagsAfterPartitionStart(false);
        }
        catch (Exception e)

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run nodetool verify / sstableverify on the sstable to confirm corruption.
  2. Run nodetool scrub (sstablescrub) to repair or quarantine the corrupt sstable.
  3. Restore the sstable from backup and run nodetool repair to re-replicate data.
  4. If offsets come from custom tooling, fix bound computation to land exactly one byte past the END_OF_PARTITION marker.

Example fix

// before
reader.seekToPartition(bounds.lowerPosition);
// after
try {
    reader.seekToPartition(bounds.lowerPosition);
} catch (IOException e) {
    throw new CorruptSSTableException(e, ssTableReader.getFilename()); // treat as corruption
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    cursor.seekToPartition(pos);
} catch (IOException e) {
    if (e.getMessage().contains("end-of-partition marker"))
        throw new CorruptSSTableException(e, filename); // route to scrub/verify
    throw e;
}

Prevention

When it happens

Trigger: Seeking to a partition start offset from bounds/index metadata that does not sit exactly one byte past an end-of-partition marker — mis-sized bounds landing one byte off, or Data.db corruption.

Common situations: Corrupted data files after disk failures; hand-edited or wrongly generated partition bounds; truncated Data.db shifting offsets; buggy tools computing partition start positions.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/80e50bca760dcfd5. Report an issue: GitHub.