{"record":{"id":"ec06d1eaf0b11917","repo":"Intervention/image","slug":"trim-modifier-cannot-be-applied-to-animated-images","errorCode":null,"errorMessage":"Trim modifier cannot be applied to animated images","messagePattern":"Trim modifier cannot be applied to animated images","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/Drivers/Gd/Modifiers/TrimModifier.php","lineNumber":31,"sourceCode":"use Intervention\\Image\\Interfaces\\SpecializedInterface;\nuse Intervention\\Image\\Modifiers\\TrimModifier as GenericTrimModifier;\nuse ValueError;\n\nclass TrimModifier extends GenericTrimModifier implements SpecializedInterface\n{\n    /**\n     * {@inheritdoc}\n     *\n     * @see ModifierInterface::apply()\n     *\n     * @throws NotSupportedException\n     * @throws StateException\n     * @throws ModifierException\n     */\n    public function apply(ImageInterface $image): ImageInterface\n    {\n        if ($image->isAnimated()) {\n            throw new NotSupportedException('Trim modifier cannot be applied to animated images');\n        }\n\n        $canvas = $this->transparencySafeVersion($image->core()->native());\n\n        // apply tolerance with a min. value of .5 because the default tolerance of '0' should\n        // already trim away similar colors which is not the case with imagecropauto.\n        $trimmed = imagecropauto(\n            $canvas,\n            IMG_CROP_THRESHOLD,\n            max([.5, $this->tolerance / 10]),\n            $this->trimColor($image),\n        );\n\n        // if the tolerance is very high, it is possible that no image is left.\n        // imagick returns a 1x1 pixel image in this case. this does the same.\n        if ($trimmed === false) {\n            $trimmed = $this->driver()->createImage(1, 1)->core()->native();\n        }","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/Intervention/image/blob/5598b9e39751c34afc5cdee84abef77f92c26f68/src/Drivers/Gd/Modifiers/TrimModifier.php#L13-L49","documentation":"GD's trim is implemented with imagecropauto() on a single native frame and has no per-frame equivalent, so the modifier refuses images whose core holds more than one frame (isAnimated() means count() > 1) with NotSupportedException. Animated GIFs and WebPs are decoded multi-frame by default (Config->decodeAnimation defaults to true), which is why trim() works on stills but throws on animations.","triggerScenarios":"$manager->decode('animated.gif')->trim(); trimming animated WebP stickers in a batch job; pipelines that trim arbitrary uploads where animations sneak in; code developed against JPEG/PNG only later fed GIFs.","commonSituations":"Avatar/border-trimming features that receive GIFs; user-upload processing of mixed content; queue workers handling animated stickers.","solutions":["Decode animations away at load time: new ImageManager(GdDriver::class, decodeAnimation: false) so only the first frame is decoded","Collapse to a single frame before trimming: $image->removeAnimation(0)->trim() (drops the animation)","Guard with if ($image->isAnimated()) and branch for animations","Catch NotSupportedException and continue without trimming"],"exampleFix":"// before\n$manager = new ImageManager(GdDriver::class);\n$image = $manager->decode('sticker.gif')->trim(); // NotSupportedException\n\n// after: decode the first frame only, then trim\nuse Intervention\\Image\\Drivers\\Gd\\Driver as GdDriver;\n$manager = new ImageManager(GdDriver::class, decodeAnimation: false);\n$image = $manager->decode('sticker.gif')->trim();","handlingStrategy":"validation","validationCode":"if ($image->isAnimated()) {\n    // GD cannot trim frame stacks: collapse to the first frame first\n    $image->removeAnimation(0);\n}\n\n$image->trim(10);\n\n// alternative: decode only the first frame from the start\n// $manager = new ImageManager(GdDriver::class, decodeAnimation: false);","typeGuard":null,"tryCatchPattern":"use Intervention\\Image\\Exceptions\\NotSupportedException;\n\ntry {\n    $image->trim(10);\n} catch (NotSupportedException $e) {\n    $logger->info('Skipped trim for animated input');\n    // continue without trimming\n}","preventionTips":["Set decodeAnimation: false on the manager when your pipeline only outputs stills.","Branch on $image->isAnimated() before geometry operations.","Include GIF/WebP fixtures in trim tests.","Collapse animations with removeAnimation(0) before frame-sensitive modifiers."],"tags":["gd","trim","animation","gif","webp","not-supported"],"backgroundTag":"animated-image-unsupported-operation","analyzedSha":"5598b9e39751c34afc5cdee84abef77f92c26f68","analyzedAt":"2026-08-23T02:17:31.068Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}