Intervention/image · error · ImageDecoderException

Failed to decode GIF format

Error message

Failed to decode GIF format

What it means

Thrown by the GD driver when a GIF is decoded with animation decoding disabled (config option decodeAnimation=false). In that mode the decoder calls imagecreatefromgif()/imagecreatefromstring() and both return false, so the raw bytes could not be parsed as a GIF at all. The input is typically truncated, corrupt, or not actually GIF data.

Source

Thrown at src/Drivers/Gd/Decoders/NativeObjectDecoder.php:82

    /**
     * Decode image from given GIF source which can be either a file path or binary data.
     *
     * Depending on the configuration, this is taken over by the native GD function
     * or, if animations are required, by our own extended decoder.
     *
     * @throws InvalidArgumentException
     * @throws ImageDecoderException
     * @throws DriverException
     * @throws StateException
     */
    protected function decodeGif(string $input): ImageInterface
    {
        // create non-animated image depending on config
        if ($this->driver()->config()->decodeAnimation === false) {
            $native = $this->isGifFormat($input) ? @imagecreatefromstring($input) : @imagecreatefromgif($input);

            if ($native === false) {
                throw new ImageDecoderException('Failed to decode GIF format');
            }

            $image = self::decode($native);
            $image->origin()->setMediaType('image/gif');

            return $image;
        }

        try {
            // create empty core
            $core = new Core();

            // add frames to core
            $splitter = GifSplitter::decode($input)
                ->split()
                ->flatten()
                ->each(function (GdImage $native, int $delay) use ($core): void {
                    $core->push(new Frame($native, $delay / 100));

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Verify the input really is a GIF before decoding: (new finfo())->buffer($data) or getimagesize()
  2. If corrupt, re-download or reject the source instead of retrying the same bytes
  3. Check gd_info() in the failing environment includes GIF support
  4. Switch the manager to the Imagick driver (ImageManager::usingDriver(ImagickDriver::class)) which tolerates more GIF variants
  5. Wrap reads in try/catch on ImageDecoderException and quarantine bad files

Example fix

// before
$image = $manager->read($uploadPath); // throws on corrupt GIF

// after
$mime = (new finfo())->file($uploadPath);
if (!str_starts_with($mime, 'image/')) {
    throw new RuntimeException('Unsupported or corrupt image: ' . $mime);
}
$image = $manager->read($uploadPath);
Defensive patterns

Strategy: try-catch

Validate before calling

$data = file_get_contents($path);
if (!str_starts_with((new finfo())->buffer($data), 'image/gif')) {
    throw new InvalidArgumentException('Source is not a GIF');
}
$manager->read($data);

Try / catch

try {
    $image = $manager->read($gifData);
} catch (ImageDecoderException $e) {
    // reject upload, log, or re-fetch the source
}

Prevention

When it happens

Trigger: ImageManager with the GD driver and option ['decodeAnimation' => false], then ->read($gifData) where $gifData is a truncated upload, a renamed non-GIF file (e.g. an HTML error page saved as .gif), or a GIF on a GD build without GIF read support.

Common situations: User uploads cut off mid-transfer (upload_max_filesize or client abort), fetching remote images without checking the HTTP status so an error body is decoded, files corrupted by ASCII-mode FTP transfers, unusual shared-hosting GD builds.

Understand the failure class

Related errors


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