{"record":{"id":"18aaf7823b295202","repo":"HMCL-dev/HMCL","slug":"unsupported-disposeop-at-frame","errorCode":null,"errorMessage":"Unsupported disposeOp  at frame ","messagePattern":"Unsupported disposeOp  at frame ","errorType":"validation","errorClass":"PngIntegrityException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/ui/image/ImageUtils.java","lineNumber":699,"sourceCode":"\n                durations[frameIndex] = durationsMills;\n            }\n\n            switch (control.disposeOp) {\n                case 0:  // APNG_DISPOST_OP_NONE\n                    System.arraycopy(currentFrameBuffer, 0, buffer, 0, currentFrameBuffer.length);\n                    break;\n                case 1: // APNG_DISPOSE_OP_BACKGROUND\n                    for (int row = 0; row < control.height; row++) {\n                        int fromIndex = (control.yOffset + row) * width + control.xOffset;\n                        Arrays.fill(buffer, fromIndex, fromIndex + control.width, 0);\n                    }\n                    break;\n                case 2: // APNG_DISPOSE_OP_PREVIOUS\n                    // Do nothing, keep the previous frame.\n                    break;\n                default:\n                    throw new PngIntegrityException(\"Unsupported disposeOp \" + control.disposeOp + \" at frame \" + frameIndex);\n            }\n        }\n\n        @Nullable PngAnimationControl animationControl = sequence.getAnimationControl();\n        int cycleCount;\n        if (animationControl != null) {\n            cycleCount = animationControl.numPlays();\n            if (cycleCount == 0)\n                cycleCount = Timeline.INDEFINITE;\n        } else {\n            cycleCount = Timeline.INDEFINITE;\n        }\n\n        if (doScale)\n            return new AnimationImageImpl(targetWidth, targetHeight, framePixels, durations, cycleCount);\n        else\n            return new AnimationImageImpl(width, height, framePixels, durations, cycleCount);\n    }","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/ui/image/ImageUtils.java#L681-L717","documentation":"Thrown by ImageUtils.toImage as PngIntegrityException when an APNG frame's dispose_op is not 0 (NONE), 1 (BACKGROUND), or 2 (PREVIOUS) — the only operations defined by the APNG spec. Indicates a corrupt or invalid fcTL chunk.","triggerScenarios":"Decoding an APNG whose fcTL dispose_op byte holds a value > 2, typically from file corruption or a deliberately malformed image.","commonSituations":"Damaged downloads; corrupted game/resource-pack assets; adversarial PNG files in untrusted content.","solutions":["Re-encode or re-download the APNG — valid files never contain dispose_op > 2","Catch PngIntegrityException and use the PNG's static default image instead of the animation","Validate fcTL fields at parse time and surface a clear 'corrupt APNG' error","Run the file through apngcheck/ffmpeg to confirm integrity before decoding"],"exampleFix":"// before\nImage anim = ImageUtils.createAnimationFromAPNGApngStream(stream);\n// after\nImage anim;\ntry {\n    anim = ImageUtils.createAnimationFromAPNGApngStream(stream);\n} catch (PngIntegrityException e) {\n    anim = new Image(stream); // static fallback\n}","handlingStrategy":"try-catch","validationCode":"if (control.disposeOp < 0 || control.disposeOp > 2) {\n    throw new IllegalArgumentException(\"Non-spec disposeOp: \" + control.disposeOp);\n}","typeGuard":"static boolean hasKnownDisposeOp(PngFrameControl c) {\n    return c.disposeOp >= 0 && c.disposeOp <= 2; // NONE, BACKGROUND, PREVIOUS\n}","tryCatchPattern":"try {\n    animation = ImageUtils.toImage(sequence, ...);\n} catch (PngIntegrityException e) {\n    animation = null;\n    useStaticFrame();\n}","preventionTips":["Checksum-verify image assets after download","Treat out-of-range chunk enum values as a corrupted-file signal","Maintain a graceful static-image fallback for animations","Sanity-check all fcTL fields immediately after chunk parsing"],"tags":["apng","unsupported-value","png-validation"],"backgroundTag":"unsupported-enum-value","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"}