HMCL-dev/HMCL · error · PngIntegrityException
bKGD chunk received before IHDR chunk
Error message
bKGD chunk received before IHDR chunk
What it means
This ordering guard in readBackgroundChunk fires when a bKGD chunk appears in the stream before the IHDR header chunk has been processed. The PNG spec requires IHDR to be the first chunk; a background chunk without a header means the stream is out of order or malformed, and the reader cannot interpret the background against an unknown colour type.
Solutions
- Ensure decoding starts at the file beginning so IHDR is processed first.
- Validate chunk ordering before feeding the stream to the reader (IHDR must be first chunk).
- Re-export the PNG with a compliant tool to fix chunk order.
- Catch PngIntegrityException and reject the malformed file.
Example fix
// before: feeding arbitrary chunk stream
reader.readChunk(source, code, len);
// after: require header first
if (!headerSeen) {
throw new PngIntegrityException("bKGD before IHDR");
}
reader.readBackgroundChunk(source, len); Defensive patterns
Strategy: try-catch
Validate before calling
if (!headerSeen && chunkCodeIsBackground(code)) throw new IllegalStateException("bKGD before IHDR"); Try / catch
try { readChunks(stream); } catch (PngIntegrityException e) { if (e.getMessage().contains("before IHDR")) rejectMalformedOrdering(file); } Prevention
- Always start decoding from the file's first chunk (IHDR).
- Validate chunk ordering before parsing (IHDR first, PLTE before IDAT/tRNS/bKGD).
- Re-encode malformed files.
- Never resume decoding mid-stream without resynchronizing at a chunk boundary.
When it happens
Trigger: A stream where bKGD appears before IHDR, e.g. a malformed file or a reader resumed mid-stream at the wrong offset.
Common situations: Corrupted or hand-assembled PNGs, chunk streams spliced by tools, resuming a partial decode without skipping to IHDR.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Received tRNS data length is invalid. Should be >1 && <
- Invalid bit depth
- Indexed images (colour type ) cannot have bitdepth > 8 (bit…
- Illegal combination of bit depth
- Invalid palette data length
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/5edf30d88492c528.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/DefaultPngChunkReader.java:139
// if (dataLength % 3 != 0) {
// throw new PngIntegrityException(String.format("png spec: palette chunk length must be divisible by 3: %d", dataLength));
// }
if (source.supportsByteAccess()) {
processor.processTransparency(source.getBytes(), source.tell(), dataLength);
source.skip(dataLength);
} else {
byte[] paletteBytes = new byte[dataLength];
//ByteStreams.readFully(source.getBis(), paletteBytes);
source.getDis().readFully(paletteBytes);
processor.processTransparency(paletteBytes, 0, dataLength);
}
}
@Override
public void readBackgroundChunk(PngSource source, int dataLength) throws IOException, PngException {
if (!seenHeader) {
throw new PngIntegrityException("bKGD chunk received before IHDR chunk");
}
// TODO
//processor.processBackground(PngBackground.from(source, dataLength);
source.skip(dataLength);
}
@Override
public void readPaletteChunk(PngSource source, int dataLength) throws IOException, PngException {
if (dataLength % 3 != 0) {
throw new PngIntegrityException(String.format("png spec: palette chunk length must be divisible by 3: %d", dataLength));
}
// TODO: can check if colour type matches palette type, or if any palette received before (overkill?)
if (source.supportsByteAccess()) {
processor.processPalette(source.getBytes(), source.tell(), dataLength);
source.skip(dataLength);
} else {View on GitHub (pinned to 24702dc5a0)