HMCL-dev/HMCL · error · PngException

ERROR_EOF_EXPECTED

ERROR_EOF_EXPECTED

Error message

Completed IEND but %d byte(s) remain

What it means

A conformant PNG stream ends at the IEND chunk; any bytes after IEND are unexpected trailing data. After the chunk loop finishes, PngReadHelper.read checks source.available() and throws PngException with code ERROR_EOF_EXPECTED when leftover bytes remain, indicating a malformed or concatenated stream.

Solutions

  1. Inspect the file for data after the IEND chunk and strip the trailing bytes.
  2. Verify chunk lengths with pngcheck to find where parsing desynchronized.
  3. Re-export/re-save the image with a standard tool to normalize the file.
  4. Treat it as user-facing corruption: re-download the asset rather than trimming blindly.

Example fix

// before: file has trailing garbage after IEND
PngReadHelper.read(is, reader); // Completed IEND but 1024 byte(s) remain
// after: trim to end of IEND chunk
byte[] png = trimAfterIEND(Files.readAllBytes(file));
PngReadHelper.read(new ByteArrayInputStream(png), reader);
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure nothing follows IEND
static long bytesAfterIEND(Path f) throws IOException {
  byte[] d = Files.readAllBytes(f);
  long off = 8;
  while (off + 8 <= d.length) {
    int len = ((d[(int)off]&0xff)<<24)|((d[(int)off+1]&0xff)<<16)|((d[(int)off+2]&0xff)<<8)|(d[(int)off+3]&0xff);
    String type = new String(d, (int) off + 4, 4, StandardCharsets.US_ASCII);
    off += 12 + len;
    if (type.equals("IEND")) return d.length - off;
  }
  return -1; // no IEND: different error
}

Try / catch

try {
  PngReadHelper.read(is, reader);
} catch (PngException e) {
  if (e.getCode() == PngConstants.ERROR_EOF_EXPECTED) {
    LOG.warning("trailing data after IEND; sanitizing file");
    is = new ByteArrayInputStream(trimAfterIEND(raw));
    return PngReadHelper.read(is, reader);
  }
  throw e;
}

Prevention

When it happens

Trigger: Reading a stream with data appended after IEND — e.g. two PNGs concatenated, a PNG with an appended archive/signature block, or a stream where a chunk length mis-parse caused IEND to be detected early.

Common situations: Files padded or signed by tools (some distributors append data); naive concatenation of images; corrupted chunk length fields causing desynchronization so IEND is read at the wrong offset.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/PngReadHelper.java:71

     */
    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);

            return reader.getResult();

        } catch (EOFException e) {
            throw new PngException(PngConstants.ERROR_EOF, "Unexpected EOF", e);
        } catch (IOException e) {
            throw new PngException(PngConstants.ERROR_UNKNOWN_IO_FAILURE, e.getMessage(), e);
        }

    }

    /**
     * Number of bytes per row is key to processing scanlines.
     * <p>
     * TODO: should this by on the header?

View on GitHub (pinned to 24702dc5a0)