apache/druid · error · RuntimeException
Invalid frame file trailer for object :
Error message
Invalid frame file trailer for object :
What it means
openPartitionedChannel validates each written frame file by reading its trailer (the last FrameFileWriter.TRAILER_LENGTH bytes) and throws a RuntimeException when the trailer bytes read is shorter than the expected trailer length. This means the frame file is truncated or corrupt and its metadata (frame sizes/counts) cannot be parsed.
Solutions
- Rerun the query so the producer rewrites the partition output file.
- Check the producing task logs for crash/disk-full errors and verify the file size in deep storage against the expected channel size.
- Check task-runner reliability (avoid killing tasks mid-write) and deep-storage upload integrity (multipart completion).
- Verify all Druid services run compatible MSQ versions to avoid format mismatches.
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
// sanity-check remote object size before parsing the trailer
long size = storageConnector.size(fileName);
if (size <= FrameFileWriter.TRAILER_LENGTH) {
throw new IllegalStateException("Frame file suspiciously small/truncated: " + fileName);
} Try / catch
try {
readPartitionOutput();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Invalid frame file trailer")) {
rerunQuery(); // truncated output must be regenerated
} else { throw e; }
} Prevention
- Ensure task workers are not OOM-killed or preempted mid-write.
- Monitor task disks for full-disk conditions during large shuffles.
- Keep MSQ engine versions uniform across the cluster.
When it happens
Trigger: Reading the trailer of the partition output file via FileChannel.read returns fewer bytes than TRAILER_LENGTH: the file was written incompletely (producer crash/disk full), uploaded partially to deep storage, or is otherwise truncated/corrupt.
Common situations: Worker killed (OOM/preemption) while writing output; interrupted upload leaving a partial object; disk full on the task's tmp dir; version mismatch causing wrong trailer format expectations.
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
- File[ ] is too short (size[%,d])
- Bad null-marker byte, delegate class
- Broadcast input number out of range
- BroadcastTablesTooLarge
- bucketSize must be a power of two but was[%,d]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2964861cdc3bfc06.
Report an issue: GitHub.
Appendix: source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/shuffle/output/DurableStorageTaskOutputChannelFactory.java:184
);
final Supplier<Long> channelSizeSupplier = countingOutputStream::getCount;
final File footerFile = new File(tmpDir, fileName + "_footer");
// build supplier for reading the footer of the underlying frame file
final Supplier<FrameFileFooter> frameFileFooterSupplier = Suppliers.memoize(() -> {
try {
// read trailer and find the footer size
byte[] trailerBytes = new byte[FrameFileWriter.TRAILER_LENGTH];
long channelSize = channelSizeSupplier.get();
try (InputStream reader = storageConnector.readRange(
fileName,
channelSize - FrameFileWriter.TRAILER_LENGTH,
FrameFileWriter.TRAILER_LENGTH
)) {
int bytesRead = reader.read(trailerBytes, 0, trailerBytes.length);
if (bytesRead != FrameFileWriter.TRAILER_LENGTH) {
throw new RuntimeException("Invalid frame file trailer for object : " + fileName);
}
}
Memory trailer = Memory.wrap(trailerBytes);
int footerLength = trailer.getInt(Integer.BYTES * 2L);
// read the footer into a file and map it to memory
FileUtils.mkdirp(footerFile.getParentFile());
Preconditions.checkState(footerFile.createNewFile(), "Unable to create local footer file");
try (FileOutputStream footerFileStream = new FileOutputStream(footerFile);
InputStream footerInputStream =
storageConnector.readRange(fileName, channelSize - footerLength, footerLength)) {
IOUtils.copy(footerInputStream, footerFileStream);
}
MappedByteBufferHandler mapHandle = FileUtils.map(footerFile);
Memory footerMemory = Memory.wrap(mapHandle.get(), ByteOrder.LITTLE_ENDIAN);
// create a frame file footer from the mapper memoryView on GitHub (pinned to 9b90983fd2)