HMCL-dev/HMCL · error · PngIntegrityException
tRNS chunk for greyscale image must be exactly length=2, not
Error message
tRNS chunk for greyscale image must be exactly length=2, not %d
What it means
PngIntegrityException thrown when a tRNS (transparency) chunk appears in a greyscale (colour type 0) PNG but its data length is not exactly 2 bytes. Per the PNG specification, a greyscale tRNS holds exactly one 16-bit sample value identifying the transparent grey level. The processor validates the chunk length before handing the two bytes to the builder.
Solutions
- Fix the PNG so the greyscale tRNS chunk contains exactly 2 bytes (one 16-bit grey sample value)
- Re-encode or re-save the image with a compliant tool (e.g. pngcrush, ImageMagick) to regenerate a correct tRNS chunk
- If the image truly is colour-type 2, fix the IHDR colour type so the 6-byte RGB tRNS matches
- Catch PngIntegrityException and reject/fall back to decoding without transparency
Example fix
// before: tRNS data of 6 bytes on a colour-type-0 image
byte[] trns = {0, 0, 0, 0, 0, 0};
processor.processTransparency(trns, 0, 6); // throws
// after: exactly 2 bytes (transparent grey = 0x0000)
byte[] trns = {0, 0};
processor.processTransparency(trns, 0, 2); // OK Defensive patterns
Strategy: validation
Validate before calling
if (header.colourType == PngColourType.PNG_GREYSCALE && trnsData.length != 2) {
throw new IllegalArgumentException("Greyscale tRNS must be exactly 2 bytes, got " + trnsData.length);
}
processor.processTransparency(trnsData, 0, trnsData.length); Type guard
boolean hasValidGreyscaleTrns(PngHeader header, byte[] trns) {
return header.colourType == PngColourType.PNG_GREYSCALE && trns != null && trns.length == 2;
} Try / catch
try {
processor.processTransparency(bytes, position, length);
} catch (PngIntegrityException e) {
LOG.warn("Malformed tRNS chunk, ignoring transparency: " + e.getMessage());
// proceed decoding without transparency
} Prevention
- Validate chunk lengths against the PNG spec table for each colour type before dispatch
- Re-save suspicious images with a compliant tool to normalize chunks
- Never hand-edit binary PNG chunk data
- Check for truncation (chunk length vs actual available bytes) before parsing
When it happens
Trigger: Calling processTransparency on an Argb8888Processor for a PNG_GREYSCALE image with a tRNS chunk whose length is anything other than 2 — e.g. a 1-byte or truncated tRNS, or a colour-type-2-style 6-byte tRNS left in a greyscale image.
Common situations: Corrupt or hand-edited PNG files; images converted between colour types with the tRNS chunk not rewritten; PNGs produced by buggy encoders that emit malformed tRNS chunks; truncated downloads that cut the chunk data short.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Illegal to have tRNS chunk with image type
- Received tRNS data but no palette is in place
- Received tRNS data length is invalid. Should be >1 && <
- acTL chunk length must be
- fcTL chunk length must be
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/8b7b10c62e62d16d.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/argb8888/Argb8888Processor.java:69
@Override
public void processGamma(PngGamma gamma) throws PngException {
// No gamma processing is done at the moment.
}
@Override
public void processPalette(byte[] bytes, int position, int length) throws PngException {
//palette = Argb8888Palette.fromPaletteBytes(bytes, position, length);
//scanlineProcessor = Argb8888Processors.fromPalette(this.header, palette);
builder.receivePalette(Argb8888Palette.fromPaletteBytes(bytes, position, length));
}
@Override
public void processTransparency(byte[] bytes, int position, int length) throws PngException {
switch (header.colourType) {
case PNG_GREYSCALE: // colour type 0
// grey sample value (2 bytes)
if (length != 2) {
throw new PngIntegrityException(String.format("tRNS chunk for greyscale image must be exactly length=2, not %d", length));
}
builder.processTransparentGreyscale(bytes[0], bytes[1]);
break;
case PNG_TRUECOLOUR: // colour type 2
// red, green, blue samples, EACH with two bytes (16-bits)
builder.processTransparentTruecolour(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]);
break;
case PNG_INDEXED_COLOUR: // colour type 3
// This is a sequence of one-byte alpha values to apply to each palette entry starting at zero.
// The number of entries may be less than the size of the palette, but not more.
builder.processTransparentPalette(bytes, position, length);
break;
case PNG_GREYSCALE_WITH_ALPHA:
case PNG_TRUECOLOUR_WITH_ALPHA:
default:View on GitHub (pinned to 24702dc5a0)