{"record":{"id":"c1a07587da500ccf","repo":"Intervention/image","slug":"failed-to-build-watermark-image","errorCode":null,"errorMessage":"Failed to build watermark image","messagePattern":"Failed to build watermark image","errorType":"exception","errorClass":"ModifierException","httpStatus":null,"severity":"error","filePath":"src/Drivers/Gd/Modifiers/InsertModifier.php","lineNumber":78,"sourceCode":"        }\n    }\n\n    /**\n     * Build a faded copy of the watermark by scaling each pixel's alpha\n     * by the requested transparency factor of the modifier. Created once\n     * and reused for every frame.\n     *\n     * @throws ModifierException\n     */\n    private function fadeWatermark(ImageInterface $watermark): ImageInterface\n    {\n        $width = $watermark->width();\n        $height = $watermark->height();\n\n        $faded = imagecreatetruecolor($width, $height);\n\n        if ($faded === false) {\n            throw new ModifierException('Failed to build watermark image');\n        }\n\n        imagealphablending($faded, false);\n        imagesavealpha($faded, true);\n\n        $watermarkNative = $watermark->core()->native();\n\n        for ($y = 0; $y < $height; $y++) {\n            for ($x = 0; $x < $width; $x++) {\n                $color = imagecolorat($watermarkNative, $x, $y);\n                $alpha = ($color >> 24) & 0x7F;\n                // GD stores alpha as 0 (opaque) … 127 (transparent), so scale\n                // the opacity (127 - alpha) and flip back to GD's convention.\n                $newAlpha = 127 - (int) round((127 - $alpha) * $this->transparency);\n                imagesetpixel($faded, $x, $y, ($newAlpha << 24) | ($color & 0xFFFFFF));\n            }\n        }\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/Intervention/image/blob/5598b9e39751c34afc5cdee84abef77f92c26f68/src/Drivers/Gd/Modifiers/InsertModifier.php#L60-L96","documentation":"When insert() is called with transparency below 1.0, the GD driver builds a faded copy of the watermark on a new truecolor canvas of the same dimensions (imagecreatetruecolor). That call returns false when PHP cannot allocate the memory - roughly width x height x 4 bytes per canvas - and the modifier turns it into ModifierException 'Failed to build watermark image'. With the default transparency of 1.0 this code path is skipped entirely, so the error is specific to faded watermarks.","triggerScenarios":"$image->insert($hugeImage, transparency: 0.5) with dimensions large relative to memory_limit (e.g. 8000x6000 needs about 183 MB per canvas); low memory_limit on shared hosting or CLI workers; pipelines already holding a large base image plus the watermark.","commonSituations":"Full-resolution watermarks on print-sized images; default 128M memory_limit with large assets; batch jobs processing many images in one process without freeing memory.","solutions":["Pre-scale the watermark to its real display size before inserting it","Raise the memory limit for the job: ini_set('memory_limit', '512M')","Skip fading (transparency 1.0) - no extra canvas is allocated","Catch ModifierException and fall back to inserting without transparency"],"exampleFix":"// before: fading a 6000x4000 watermark on a small memory limit\n$image->insert($watermarkPath, transparency: 0.5);\n\n// after: pre-scale the watermark, then fade it\nuse Intervention\\Image\\ImageManager;\nuse Intervention\\Image\\Drivers\\Gd\\Driver as GdDriver;\n\n$manager = new ImageManager(GdDriver::class);\n$watermark = $manager->decode(file_get_contents($watermarkPath))->scale(600);\n$image->insert($watermark, transparency: 0.5);","handlingStrategy":"fallback","validationCode":"// rough memory check before fading a large watermark\n$info = is_string($watermarkPath) ? getimagesize($watermarkPath) : false;\n\nif ($info !== false) {\n    [$width, $height] = $info;\n    $requiredBytes = $width * $height * 4 * 2; // watermark + faded copy\n    $limitBytes = parse_size_shorthand(ini_get('memory_limit')); // e.g. '128M' -> bytes\n\n    if (memory_get_usage(true) + $requiredBytes > $limitBytes) {\n        // pre-scale to avoid the allocation failure\n        $watermark = $manager->decode(file_get_contents($watermarkPath))->scale(600);\n        $image->insert($watermark, transparency: 0.5);\n        return;\n    }\n}\n\n$image->insert($watermarkPath, transparency: 0.5);","typeGuard":null,"tryCatchPattern":"use Intervention\\Image\\Exceptions\\ModifierException;\n\ntry {\n    $image->insert($watermark, transparency: 0.5);\n} catch (ModifierException $e) {\n    // memory allocation failed: insert without the fade effect\n    $image->insert($watermark);\n}","preventionTips":["Pre-scale watermarks to their real display size instead of fading full-resolution files.","Budget roughly 4 bytes per pixel per canvas when estimating memory for GD operations.","Raise memory_limit for batch jobs, not per-request code paths.","Avoid transparency < 1.0 for very large watermarks."],"tags":["gd","insert","watermark","memory","allocation","transparency"],"backgroundTag":"gd-memory-exhausted","analyzedSha":"5598b9e39751c34afc5cdee84abef77f92c26f68","analyzedAt":"2026-08-23T02:17:31.068Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}