HMCL-dev/HMCL · error · PngException

ERROR_EOF

ERROR_EOF

Error message

Unexpected EOF

What it means

The stream ended before the PNG structure was complete (before IEND). PngReadHelper.read catches EOFException and rethrows it as PngException with code ERROR_EOF and message 'Unexpected EOF', so callers get one uniform error type instead of a raw EOFException.

Solutions

  1. Check the file/stream size and re-download or re-read it completely.
  2. Confirm the file ends with an IEND chunk; if not, the transfer was truncated.
  3. Verify the HTTP transfer completed (Content-Length matched) before decoding.
  4. Catch PngException with code ERROR_EOF and prompt the user to re-fetch the image.

Example fix

// before: decode possibly incomplete stream
PngReadHelper.read(is, reader); // Unexpected EOF
// after: ensure fully downloaded first
byte[] all = is.readAllBytes();
if (endsWithIEND(all)) PngReadHelper.read(new ByteArrayInputStream(all), reader);
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm stream is complete before decoding
byte[] all = is.readAllBytes();
boolean complete = all.length >= 12 && java.util.Arrays.equals(
    java.util.Arrays.copyOfRange(all, all.length - 12, all.length - 4),
    "IEND".getBytes(StandardCharsets.US_ASCII)) == false
    ? endsWithIendChunk(all) : endsWithIendChunk(all);

Try / catch

try {
  PngReadHelper.read(is, reader);
} catch (PngException e) {
  if (e.getCode() == PngConstants.ERROR_EOF) {
    throw new IOException("Image download incomplete, please retry", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Truncated InputStream: network disconnect mid-download, partially written file, reading only a prefix of the file, or a chunk length field pointing beyond the actual data so reads run off the end.

Common situations: Interrupted downloads; reading a file still being written; serving images with a wrong Content-Length causing early cutoff; decoding a fragment of a file.

Related errors


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

Appendix: source

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

            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?
     */
    public static int calculateBytesPerRow(int pixelsPerRow, byte bitDepth, PngColourType colourType, byte interlaceMethod) {
        if (interlaceMethod != 0) {
            throw new IllegalStateException("Interlaced images not yet supported");

        } else {
            int numComponentsPerPixel = colourType.componentsPerPixel; // or "channels". e.g. "gray and alpha" means two components.
            int bitsPerComponent = bitDepth; // e.g. "4" means 4 bits for gray, 4 bits for alpha

View on GitHub (pinned to 24702dc5a0)