Intervention/image · error · NotSupportedException

Trim modifier cannot be applied to animated images

Error message

Trim modifier cannot be applied to animated images

What it means

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.

Source

Thrown at src/Drivers/Gd/Modifiers/TrimModifier.php:31

use Intervention\Image\Interfaces\SpecializedInterface;
use Intervention\Image\Modifiers\TrimModifier as GenericTrimModifier;
use ValueError;

class TrimModifier extends GenericTrimModifier implements SpecializedInterface
{
    /**
     * {@inheritdoc}
     *
     * @see ModifierInterface::apply()
     *
     * @throws NotSupportedException
     * @throws StateException
     * @throws ModifierException
     */
    public function apply(ImageInterface $image): ImageInterface
    {
        if ($image->isAnimated()) {
            throw new NotSupportedException('Trim modifier cannot be applied to animated images');
        }

        $canvas = $this->transparencySafeVersion($image->core()->native());

        // apply tolerance with a min. value of .5 because the default tolerance of '0' should
        // already trim away similar colors which is not the case with imagecropauto.
        $trimmed = imagecropauto(
            $canvas,
            IMG_CROP_THRESHOLD,
            max([.5, $this->tolerance / 10]),
            $this->trimColor($image),
        );

        // if the tolerance is very high, it is possible that no image is left.
        // imagick returns a 1x1 pixel image in this case. this does the same.
        if ($trimmed === false) {
            $trimmed = $this->driver()->createImage(1, 1)->core()->native();
        }

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Decode animations away at load time: new ImageManager(GdDriver::class, decodeAnimation: false) so only the first frame is decoded
  2. Collapse to a single frame before trimming: $image->removeAnimation(0)->trim() (drops the animation)
  3. Guard with if ($image->isAnimated()) and branch for animations
  4. Catch NotSupportedException and continue without trimming

Example fix

// before
$manager = new ImageManager(GdDriver::class);
$image = $manager->decode('sticker.gif')->trim(); // NotSupportedException

// after: decode the first frame only, then trim
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
$manager = new ImageManager(GdDriver::class, decodeAnimation: false);
$image = $manager->decode('sticker.gif')->trim();
Defensive patterns

Strategy: validation

Validate before calling

if ($image->isAnimated()) {
    // GD cannot trim frame stacks: collapse to the first frame first
    $image->removeAnimation(0);
}

$image->trim(10);

// alternative: decode only the first frame from the start
// $manager = new ImageManager(GdDriver::class, decodeAnimation: false);

Try / catch

use Intervention\Image\Exceptions\NotSupportedException;

try {
    $image->trim(10);
} catch (NotSupportedException $e) {
    $logger->info('Skipped trim for animated input');
    // continue without trimming
}

Prevention

When it happens

Trigger: $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.

Common situations: Avatar/border-trimming features that receive GIFs; user-upload processing of mixed content; queue workers handling animated stickers.

Related errors


AI-assisted analysis of Intervention/image@5598b9e397 (2026-08-23). Data as JSON: /api/errors/ec06d1eaf0b11917. Report an issue: GitHub.