HMCL-dev/HMCL · error · PngIntegrityException
fcTL chunk length must be
Error message
fcTL chunk length must be %d, not %d
What it means
APNG's fcTL (frame control) chunk is defined by the spec to have an exact length of 26 bytes (PngConstants.LENGTH_fcTL_CHUNK). DefaultPngChunkReader.readFrameControlChunk validates the declared chunk data length before parsing the sequence number and frame parameters; a mismatch means the file is structurally corrupt per the APNG specification, so it throws PngIntegrityException rather than reading garbage.
Solutions
- Re-obtain the PNG file from a trusted source and verify integrity (checksum/signature).
- Re-encode the APNG with a conformant encoder (e.g. ffmpeg, apngasm).
- Validate the file with a PNG/APNG checker (pngcheck) before feeding it to this reader.
- If the length really is 26 but constants disagree, check that your PngConstants matches the APNG spec.
Example fix
// before: reading a possibly corrupt file directly reader.read(inputStream, apngReader); // after: validate/repair the source first boolean ok = PngReadHelper.readSignature(is) && pngCheckPasses(file); if (ok) reader.read(is, apngReader); else reDownloadOrReencode(file);
Defensive patterns
Strategy: validation
Validate before calling
// verify chunk lengths before parsing
long off = 8; // after signature
while (off + 8 <= file.length()) {
int len = readIntBE(data, off);
String type = new String(data, (int) off + 4, 4, StandardCharsets.US_ASCII);
if (type.equals("fcTL") && len != 26) throw new IllegalArgumentException("bad fcTL length " + len);
off += 12 + len; // length+type+CRC
} Try / catch
try {
PngReadHelper.read(is, apngReader);
} catch (PngException e) {
if (e instanceof PngIntegrityException) showCorruptFileDialog();
else throw e;
} Prevention
- Run pngcheck -v on APNG assets before shipping/decoding them.
- Re-encode untrusted APNGs with a known-good encoder.
- Treat PngIntegrityException as corrupt-file signal, not a code bug.
- Keep files checksummed at download time and verify before parsing.
When it happens
Trigger: Parsing a PNG whose fcTL chunk declares a data length other than 26 — e.g. a truncated, hand-edited, or non-conformant APNG file passed to PngReadHelper.read / DefaultPngChunkReader.readChunk.
Common situations: Corrupted downloads of animated PNGs; files produced by broken APNG encoders or post-processing tools that rewrote chunks; concatenated/truncated streams; manually modified chunk payloads.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- fctl chunk expected sequence
- fdAT chunk expected sequence
- tRNS chunk for greyscale image must be exactly length=2, not
- Illegal to have tRNS chunk with image type
- Invalid greyscale bit-depth
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/b286d3e0484a6d7f.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/DefaultPngChunkReader.java:202
default:
processor.processDefaultImageData(source.slice(dataLength), PngChunkCode.IDAT, source.tell(), dataLength);
break;
}
// source.skip(dataLength);
}
@Override
public void readAnimationControlChunk(PngSource source, int dataLength) throws IOException, PngException {
if (dataLength != PngConstants.LENGTH_acTL_CHUNK) {
throw new PngIntegrityException(String.format("acTL chunk length must be %d, not %d", PngConstants.LENGTH_acTL_CHUNK, dataLength));
}
processor.processAnimationControl(new PngAnimationControl(source.readInt(), source.readInt()));
}
@Override
public void readFrameControlChunk(PngSource source, int dataLength) throws IOException, PngException {
if (dataLength != PngConstants.LENGTH_fcTL_CHUNK) {
throw new PngIntegrityException(String.format("fcTL chunk length must be %d, not %d", PngConstants.LENGTH_fcTL_CHUNK, dataLength));
}
int sequence = source.readInt(); // TODO: check sequence # is correct or PngIntegrityException
if (sequence != apngSequenceExpect) {
throw new PngIntegrityException(String.format("fctl chunk expected sequence %d but received %d", apngSequenceExpect, sequence));
}
apngSequenceExpect++; // ready for next time
PngFrameControl frame = new PngFrameControl(
sequence,
source.readInt(), // width
source.readInt(), // height
source.readInt(), // x offset
source.readInt(), // y offset
source.readUnsignedShort(), // delay numerator
source.readUnsignedShort(), // delay denominator
source.readByte(), // dispose op
source.readByte() // blend opView on GitHub (pinned to 24702dc5a0)