HMCL-dev/HMCL · error · PngIntegrityException

Received tRNS data but no palette is in place

Error message

Received tRNS data but no palette is in place

What it means

PngIntegrityException thrown when a tRNS chunk arrives for a palette-indexed image but no PLTE-derived palette has been registered with the scanline processor. Transparency for indexed colour is expressed as alpha per palette entry, so applying tRNS without a palette is meaningless and indicates chunks arrived out of order or the PLTE chunk was missing.

Solutions

  1. Ensure the PLTE chunk is processed (processPalette called) before tRNS reaches processTransparentPalette — fix chunk ordering in the parser
  2. Reject PNGs whose tRNS appears without a preceding PLTE as structurally invalid
  3. If a chunk filter is in use, stop filtering out PLTE when tRNS is present
  4. Catch PngIntegrityException and treat the stream as malformed or skip transparency handling

Example fix

// before: tRNS delivered before PLTE
director.processTransparentPalette(trns, 0, trns.length); // throws: no palette
// after: process palette first
director.processPalette(plte, 0, plte.length);
director.processTransparentPalette(trns, 0, trns.length); // OK
Defensive patterns

Strategy: validation

Validate before calling

if (chunkSeen("tRNS") && !chunkSeen("PLTE")) {
    throw new IllegalArgumentException("tRNS chunk requires a preceding PLTE chunk");
}

Type guard

boolean paletteReady(Argb8888Director director) {
    return director.scanlineProcessor.getPalette() != null;
}

Try / catch

try {
    director.processTransparentPalette(bytes, position, length);
} catch (PngIntegrityException e) {
    LOG.warn("tRNS without palette (missing/out-of-order PLTE): " + e.getMessage());
    // treat stream as malformed or continue without transparency
}

Prevention

When it happens

Trigger: Calling processTransparentPalette on a BasicArgb8888Director when scanlineProcessor.getPalette() returns null — i.e. processPalette was never called (or the PLTE chunk was absent/skipped) before the tRNS chunk was processed.

Common situations: PNG streams whose tRNS precedes PLTE or whose PLTE was dropped by a chunk filter; truncated palette images missing PLTE; custom parsers feeding chunks to the director in the wrong order; files malformed by broken muxers.

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


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/1c7ccbd541f6ef5e. Report an issue: GitHub.

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/argb8888/BasicArgb8888Director.java:24

import org.jackhuang.hmcl.ui.image.apng.error.PngException;
import org.jackhuang.hmcl.ui.image.apng.error.PngIntegrityException;

/**
 * Common functionality for Argb8888Director implementations.
 */
public abstract class BasicArgb8888Director<ResultT> implements Argb8888Director<ResultT> {
    protected Argb8888ScanlineProcessor scanlineProcessor;

    @Override
    public void receivePalette(Argb8888Palette palette) {
        scanlineProcessor.setPalette(palette);
    }

    @Override
    public void processTransparentPalette(byte[] bytes, int position, int length) throws PngException {
        Argb8888Palette palette = scanlineProcessor.getPalette();
        if (null == palette) {
            throw new PngIntegrityException("Received tRNS data but no palette is in place");
        }
        if (length <= 0 || length > palette.size()) {
            throw new PngIntegrityException(String.format("Received tRNS data length is invalid. Should be >1 && < %d but is %d", palette.size(), length));
        }
        for (int i = 0; i < length; i++) {
            final int alpha = 0xff & bytes[position + i];
            palette.argbArray()[i] = alpha << 24 | palette.argbArray()[i] & 0x00FFFFFF;
        }
    }

    @Override
    public void processTransparentGreyscale(byte k1, byte k0) throws PngException {
        scanlineProcessor.processTransparentGreyscale(k1, k0);
    }

    @Override
    public void processTransparentTruecolour(byte r1, byte r0, byte g1, byte g0, byte b1, byte b0) throws PngException {
        scanlineProcessor.processTransparentTruecolour(r1, r0, g1, g0, b1, b0);

View on GitHub (pinned to 24702dc5a0)