HMCL-dev/HMCL · error · PngIntegrityException
acTL chunk length must be
Error message
acTL chunk length must be %d, not %d
What it means
This guard in readAnimationControlChunk fires when an acTL chunk's data length does not equal the exact size the APNG specification requires (8 bytes: num_frames plus num_plays). Any other length means the animation control chunk is malformed, so the animation metadata cannot be trusted.
Solutions
- Re-export the APNG with a compliant tool (e.g. ffmpeg -apng or apngasm).
- Verify the file with an APNG-aware validator; confirm the acTL chunk length is 8.
- If the file is not meant to be animated, check whether a wrong chunk is being read as acTL.
- Catch PngIntegrityException and reject the malformed animation metadata.
Example fix
// before
reader.readAnimationControlChunk(source, dataLength);
// after
if (dataLength != 8) {
throw new PngIntegrityException("acTL must be 8 bytes, got " + dataLength);
}
reader.readAnimationControlChunk(source, dataLength); Defensive patterns
Strategy: try-catch
Validate before calling
if (code == acTL && dataLength != 8) throw new IllegalStateException("acTL must be exactly 8 bytes"); Try / catch
try { reader.readAnimationControlChunk(source, dataLength); } catch (PngIntegrityException e) { failDecode("malformed acTL: " + e.getMessage()); } Prevention
- Produce APNGs with compliant tools (apngasm, ffmpeg).
- Validate acTL length == 8 during pre-scan.
- Confirm the file is really an APNG before parsing animation chunks.
- Handle PngIntegrityException to fall back to static first-frame rendering.
When it happens
Trigger: An APNG whose acTL chunk dataLength differs from 8, due to corruption, truncation, or a non-conformant APNG writer.
Common situations: Corrupted animated PNG downloads, files produced by experimental APNG encoders, misidentified chunk boundaries.
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
- Received tRNS data length is invalid. Should be >1 && <
- Valid PNG colour types are 0, 2, 3, 4, 6. Type
- Valid greyscale bit depths are 1, 2, 4, 8, not
- tRNS chunk for greyscale image must be exactly length=2, not
- Illegal to have tRNS chunk with image type
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/957be05cd6dbe6f6.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/DefaultPngChunkReader.java:194
// do nothing
source.skip(dataLength);
break;
case ANIMATED_KEEP_DEFAULT_IMAGE:
processor.processFrameImageData(source.slice(dataLength), PngChunkCode.IDAT, source.tell(), dataLength);
break;
case NOT_ANIMATED:
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,View on GitHub (pinned to 24702dc5a0)