{"id":"b657ede20ac93821","repo":"apache/kafka","slug":"attempt-to-truncate-log-segment-to-bytes-fai","errorCode":null,"errorMessage":"Attempt to truncate log segment {} to {} bytes failed,  size of this log segment is {} bytes.","messagePattern":"Attempt to truncate log segment (.+?) to (.+?) bytes failed,  size of this log segment is (.+?) bytes\\.","errorType":"exception","errorClass":"KafkaException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/FileRecords.java","lineNumber":281,"sourceCode":"        } finally {\n            this.file = f;\n        }\n    }\n\n    /**\n     * Truncate this file message set to the given size in bytes. Note that this API does no checking that the\n     * given size falls on a valid message boundary.\n     * In some versions of the JDK truncating to the same size as the file message set will cause an\n     * update of the files mtime, so truncate is only performed if the targetSize is smaller than the\n     * size of the underlying FileChannel.\n     * It is expected that no other threads will do writes to the log when this function is called.\n     * @param targetSize The size to truncate to. Must be between 0 and sizeInBytes.\n     * @return The number of bytes truncated off\n     */\n    public int truncateTo(int targetSize) throws IOException {\n        int originalSize = sizeInBytes();\n        if (targetSize > originalSize || targetSize < 0)\n            throw new KafkaException(\"Attempt to truncate log segment \" + file + \" to \" + targetSize + \" bytes failed, \" +\n                    \" size of this log segment is \" + originalSize + \" bytes.\");\n        if (targetSize < (int) channel.size()) {\n            channel.truncate(targetSize);\n            size.set(targetSize);\n        }\n        return originalSize - targetSize;\n    }\n\n    @Override\n    public int writeTo(TransferableChannel destChannel, int offset, int length) throws IOException {\n        long newSize = Math.min(channel.size(), end) - start;\n        int oldSize = sizeInBytes();\n        if (newSize < oldSize)\n            throw new KafkaException(String.format(\n                    \"Size of FileRecords %s has been truncated during write: old size %d, new size %d\",\n                    file.getAbsolutePath(), oldSize, newSize));\n\n        long position = start + offset;","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/FileRecords.java#L263-L299","documentation":"Thrown by FileRecords.truncateTo when targetSize is greater than the segment's current sizeInBytes or negative. Truncation can only shrink a segment, so growing or truncating to a negative offset is rejected before FileChannel.truncate is called. This is the contract documented on truncateTo.","triggerScenarios":"Calling FileRecords.truncateTo(targetSize) with targetSize > sizeInBytes() or targetSize < 0. Reachable from Log.truncateTo, LogSegment.truncateTo, recovery-point checkpointing, and KRaft snapshot truncation.","commonSituations":"A recovery or truncation routine computed a target size from a stale offset index (so targetSize points past the real end), a partition leadership handoff raced with a truncation, or an operator invoked kafka-dump-log/--truncate with a bad size. Also seen when log-start offset logic computes a negative target after metadata corruption.","solutions":["Re-read sizeInBytes() at the truncation call site and clamp targetSize to [0, sizeInBytes()] before invoking truncateTo.","Serialize truncations against the segment lock to prevent racing appends from changing size mid-call.","Validate the offset-index and time-index against the .log file size; rebuild them if they point past the real end.","If the target came from controller/KRaft metadata, ensure the broker applied metadata in order so the truncation target reflects the latest leader epoch."],"exampleFix":"// before: targetSize from a stale index entry, can exceed live size\nsegment.truncateTo(targetSize);\n\n// after: clamp to the segment's real size first\nint bounded = Math.max(0, Math.min(targetSize, segment.sizeInBytes()));\nif (bounded != targetSize) log.warn(\"Clamped truncate target from {} to {}\", targetSize, bounded);\nsegment.truncateTo(bounded);","handlingStrategy":"validation","validationCode":"int currentSize = fileRecords.sizeInBytes();\nif (targetSize < 0 || targetSize > currentSize) {\n    throw new IllegalArgumentException(\n        \"truncateTo target \" + targetSize + \" outside [0, \" + currentSize + \"]\");\n}\nfileRecords.truncateTo(targetSize);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only truncate to offsets returned by batch iteration, never to an arbitrary byte guess.","Clamp targetSize with Math.min(Math.max(0, targetSize), sizeInBytes()) when in doubt.","Remember truncateTo does not validate message boundaries; truncate only to known batch edges."],"tags":["file-records","truncate","recovery","segment","bounds"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}