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

  1. Fix the PNG so the greyscale tRNS chunk contains exactly 2 bytes (one 16-bit grey sample value)
  2. Re-encode or re-save the image with a compliant tool (e.g. pngcrush, ImageMagick) to regenerate a correct tRNS chunk
  3. If the image truly is colour-type 2, fix the IHDR colour type so the 6-byte RGB tRNS matches
  4. 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

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


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)