apache/cassandra · warning

Found a legacy log record

Error message

Found a legacy log record {} with updateTime based on the stats file, ignoring to allow startup to continue

What it means

LogFile.verifyRecord detects an sstable lifecycle log record whose update time matches the old buggy behavior where the timestamp was derived from the stats file instead of the data files. To let nodes upgraded from such a broken version start up, Cassandra logs this warning and skips the error that would otherwise be raised for 'unexpected files detected'.

Solutions

  1. No action required — Cassandra intentionally ignores the legacy record to allow startup.
  2. After a successful upgrade, verify the affected sstables with nodetool verify/scrub.
  3. Run a full compaction or major repair to rewrite sstables and regenerate clean transaction logs.
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: Startup (or txn log verification) encounters a legacy transaction record written by a patched/older Cassandra version where LogRecord.make with statsIncluded=true reproduces the same truncated update time; i.e. first upgrade from a version that based updateTime on the stats file.

Common situations: Upgrading an old cluster (pre-fix transaction log validation) to a modern version with strict record verification; broken transaction files left on disk from the older release.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/db/lifecycle/LogFile.java:287

            return;

        // Paranoid sanity checks: we create another record by looking at the files as they are
        // on disk right now and make sure the information still matches. We don't want to delete
        // files by mistake if the user has copied them from backup and forgot to remove a txn log
        // file that obsoleted the very same files. So we check the latest update time and make sure
        // it matches. Because we delete files from oldest to newest, the latest update time should
        // always match.
        record.status.onDiskRecord = record.withExistingFiles(existingFiles);
        // we can have transaction files with mismatching updateTime resolutions due to switching between jdk8 and jdk11, truncate both to be consistent:
        if (truncateMillis(record.updateTime) != truncateMillis(record.status.onDiskRecord.updateTime) && record.status.onDiskRecord.updateTime > 0)
        {
            // handle the case where we have existing broken transaction file on disk, where the update time is
            // based on the stats file. This is just for the first upgrade, patched versions never base the update
            // time on the stats file.
            LogRecord statsIncluded = LogRecord.make(record.type, existingFiles, existingFiles.size(), record.absolutePath(), true);
            if (truncateMillis(statsIncluded.updateTime) == truncateMillis(record.updateTime))
            {
                logger.warn("Found a legacy log record {} with updateTime based on the stats file, ignoring to allow startup to continue", record);
                return;
            }

            record.setError(String.format("Unexpected files detected for sstable [%s]: " +
                                          "last update time [%tc] (%d) should have been [%tc] (%d)",
                                          record.fileName(),
                                          record.status.onDiskRecord.updateTime,
                                          record.status.onDiskRecord.updateTime,
                                          record.updateTime,
                                          record.updateTime));
        }
    }

    /**
     * due to difference in timestamp resolution between jdk8 and 11 we need to return second resolution here (number
     * should end in 000): https://bugs.openjdk.java.net/browse/JDK-8177809
     */
    static long truncateMillis(long lastModified)

View on GitHub (pinned to 88fd0f6a0e)