{"record":{"id":"dc7acbd6f5fc4bc6","repo":"HMCL-dev/HMCL","slug":"error-eof-expected","errorCode":"ERROR_EOF_EXPECTED","errorMessage":"Completed IEND but %d byte(s) remain","messagePattern":"Completed IEND but (.+?) byte\\(s\\) remain","errorType":"error_code","errorClass":"PngException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/PngReadHelper.java","lineNumber":71,"sourceCode":"     */\n    public static <ResultT> ResultT read(InputStream is, PngReader<ResultT> reader) throws PngException {\n        try {\n            if (!PngReadHelper.readSignature(is)) {\n                throw new PngException(PngConstants.ERROR_NOT_PNG, \"Failed to read PNG signature\");\n            }\n\n//            PngAtOnceSource source = PngAtOnceSource.from(is);//, sourceName);\n            PngSource source = new PngStreamSource(is);\n            boolean finished = false;\n\n            while (!finished) {\n                int length = source.readInt();\n                int code = source.readInt();\n                finished = reader.readChunk(source, code, length);\n            }\n\n            if (source.available() > 0) { // Should trailing data after IEND always be error or can configure as warning?\n                throw new PngException(PngConstants.ERROR_EOF_EXPECTED, String.format(\"Completed IEND but %d byte(s) remain\", source.available()));\n            }\n\n            reader.finishedChunks(source);\n\n            return reader.getResult();\n\n        } catch (EOFException e) {\n            throw new PngException(PngConstants.ERROR_EOF, \"Unexpected EOF\", e);\n        } catch (IOException e) {\n            throw new PngException(PngConstants.ERROR_UNKNOWN_IO_FAILURE, e.getMessage(), e);\n        }\n\n    }\n\n    /**\n     * Number of bytes per row is key to processing scanlines.\n     * <p>\n     * TODO: should this by on the header?","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/ui/image/apng/reader/PngReadHelper.java#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the file for data after the IEND chunk and strip the trailing bytes.","Verify chunk lengths with pngcheck to find where parsing desynchronized.","Re-export/re-save the image with a standard tool to normalize the file.","Treat it as user-facing corruption: re-download the asset rather than trimming blindly."],"exampleFix":"// before: file has trailing garbage after IEND\nPngReadHelper.read(is, reader); // Completed IEND but 1024 byte(s) remain\n// after: trim to end of IEND chunk\nbyte[] png = trimAfterIEND(Files.readAllBytes(file));\nPngReadHelper.read(new ByteArrayInputStream(png), reader);","handlingStrategy":"try-catch","validationCode":"// ensure nothing follows IEND\nstatic long bytesAfterIEND(Path f) throws IOException {\n  byte[] d = Files.readAllBytes(f);\n  long off = 8;\n  while (off + 8 <= d.length) {\n    int len = ((d[(int)off]&0xff)<<24)|((d[(int)off+1]&0xff)<<16)|((d[(int)off+2]&0xff)<<8)|(d[(int)off+3]&0xff);\n    String type = new String(d, (int) off + 4, 4, StandardCharsets.US_ASCII);\n    off += 12 + len;\n    if (type.equals(\"IEND\")) return d.length - off;\n  }\n  return -1; // no IEND: different error\n}","typeGuard":null,"tryCatchPattern":"try {\n  PngReadHelper.read(is, reader);\n} catch (PngException e) {\n  if (e.getCode() == PngConstants.ERROR_EOF_EXPECTED) {\n    LOG.warning(\"trailing data after IEND; sanitizing file\");\n    is = new ByteArrayInputStream(trimAfterIEND(raw));\n    return PngReadHelper.read(is, reader);\n  }\n  throw e;\n}","preventionTips":["Sanitize third-party images (strip post-IEND data) before decoding.","Avoid concatenating PNG files; use archives instead.","Run pngcheck to catch bad chunk lengths that desync parsing.","Re-save suspicious files with a standard image tool."],"tags":["png","trailing-data","binary-parsing"],"backgroundTag":"unexpected-response-shape","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}