HMCL-dev/HMCL · error · PngException
ERROR_NOT_PNG
ERROR_NOT_PNG
Error message
Failed to read PNG signature
What it means
PngReadHelper.read requires the input stream to begin with the 8-byte PNG signature. readSignature returns false when the leading bytes do not match, and read throws PngException with code ERROR_NOT_PNG, meaning the stream is not a PNG at all.
Solutions
- Check the first bytes of the stream/file — they must be 89 50 4E 47 0D 0A 1A 0A.
- Unwrap any compression (e.g. GZIPInputStream) before passing the stream.
- Verify the URL/download actually returned a PNG (check Content-Type and HTTP status).
- Handle PngException with code ERROR_NOT_PNG to give the user a clear 'not a PNG' message.
Example fix
// before: passing raw (possibly gzipped or HTML) stream PngReadHelper.read(is, reader); // Failed to read PNG signature // after if (!is.markSupported()) is = new BufferedInputStream(is); is.mark(8); byte[] head = is.readNBytes(2); is.reset(); if ((head[0] & 0xFF) == 0x1F && (head[1] & 0xFF) == 0x8B) is = new GZIPInputStream(is); PngReadHelper.read(is, reader);
Defensive patterns
Strategy: validation
Validate before calling
// check PNG signature before decoding
static boolean isPng(InputStream is) throws IOException {
is.mark(8);
byte[] sig = is.readNBytes(8);
is.reset();
return java.util.Arrays.equals(sig,
new byte[]{(byte)0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A});
} Try / catch
try {
PngReadHelper.read(is, reader);
} catch (PngException e) {
if (e.getCode() == PngConstants.ERROR_NOT_PNG) {
showNotAPngMessage();
} else throw e;
} Prevention
- Check Content-Type/extension and sniff magic bytes before decoding.
- Decompress gzip streams before handing them to the reader.
- Verify HTTP status is 200 and the body is not an HTML error page.
- Never assume a .png filename means PNG content.
When it happens
Trigger: Passing a non-PNG stream (JPEG, WebP, HTML error page, empty file, gzip-compressed PNG not decompressed first) as the InputStream to PngReadHelper.read.
Common situations: Wrong file extension/content confusion after a download; server returning an HTML error page with 200; serving .png.gz without decompression; concatenating files; testing with placeholder files.
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
- ERROR_EOF_EXPECTED
- fcTL chunk length must be
- acTL chunk length must be
- ARGB8888 doesn't support PNG mode
- bKGD chunk received before IHDR chunk
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/ea34b8a20162d664.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/PngReadHelper.java:57
}
}
return true;
}
/**
* Reads a given InputStream using the PngReader to process all chunks until the file is
* finished, then returning the result from the PngReader.
*
* @param is stream to read
* @param reader reads and delegates processing of all chunks
* @param <ResultT> result of the processing
* @return result of the processing of the InputStream.
* @throws PngException
*/
public static <ResultT> ResultT read(InputStream is, PngReader<ResultT> reader) throws PngException {
try {
if (!PngReadHelper.readSignature(is)) {
throw new PngException(PngConstants.ERROR_NOT_PNG, "Failed to read PNG signature");
}
// PngAtOnceSource source = PngAtOnceSource.from(is);//, sourceName);
PngSource source = new PngStreamSource(is);
boolean finished = false;
while (!finished) {
int length = source.readInt();
int code = source.readInt();
finished = reader.readChunk(source, code, length);
}
if (source.available() > 0) { // Should trailing data after IEND always be error or can configure as warning?
throw new PngException(PngConstants.ERROR_EOF_EXPECTED, String.format("Completed IEND but %d byte(s) remain", source.available()));
}
reader.finishedChunks(source);
View on GitHub (pinned to 24702dc5a0)