Intervention/image · error · ModifierException
Failed to apply Intervention\Image\Drivers\Gd\Modifiers\Draw
Error message
Failed to apply Intervention\Image\Drivers\Gd\Modifiers\DrawBezierModifier, division by zero
What it means
For borders wider than 1px the GD driver offsets each interpolated curve segment along its normal, dividing the half border width by sqrt(dx^2 + dy^2) for every adjacent point pair. When two adjacent points of the interpolated polygon are identical the segment length is 0 and the normal is undefined, so the modifier aborts with ModifierException instead of dividing by zero. Interpolation points are cast to int, so curves whose samples collapse onto the same pixel (identical or sub-pixel spaced control points) hit this guard. It requires borderSize > 1, because 1px borders use a line-based path that tolerates duplicates.
Source
Thrown at src/Drivers/Gd/Modifiers/DrawBezierModifier.php:259
*/
private function calculateBorderSegments(array $polygon): array
{
$innerPolygon = [];
$outerPolygon = [];
$offset = $this->drawable->borderSize() / 2;
$total = count($polygon);
for ($i = 0; $i < $total; $i += 2) {
if (!array_key_exists($i + 2, $polygon) || !array_key_exists($i + 3, $polygon)) {
continue;
}
$dx = $polygon[$i + 2] - $polygon[$i];
$dy = $polygon[$i + 3] - $polygon[$i + 1];
$dxySqrt = sqrt($dx * $dx + $dy * $dy);
if ($dxySqrt === 0.0) {
throw new ModifierException('Failed to apply ' . self::class . ', division by zero');
}
$scale = $offset / $dxySqrt;
$ox = -$dy * $scale;
$oy = $dx * $scale;
$innerPolygon[] = $ox + $polygon[$i];
$innerPolygon[] = $oy + $polygon[$i + 1];
$innerPolygon[] = $ox + $polygon[$i + 2];
$innerPolygon[] = $oy + $polygon[$i + 3];
$scale = -$offset / $dxySqrt;
$ox = -$dy * $scale;
$oy = $dx * $scale;
$outerPolygon[] = $ox + $polygon[$i];
$outerPolygon[] = $oy + $polygon[$i + 1];
$outerPolygon[] = $ox + $polygon[$i + 2];View on GitHub (pinned to 5598b9e397)
Solutions
- Ensure consecutive control points are distinct - offset them by at least 1-2 pixels
- Use a 1px border (->border($color, 1)), which connects points with lines and tolerates duplicates
- Skip the border entirely for degenerate or tiny curves
- Catch ModifierException and drop/reduce the border as a fallback
Example fix
// before: consecutive control points identical + thick border
$image->drawBezier(fn($b) => $b
->point(20, 20)->point(20, 20)->point(120, 100)
->border('ff0000', 4));
// after: distinct control points
$image->drawBezier(fn($b) => $b
->point(20, 20)->point(60, 20)->point(120, 100)
->border('ff0000', 4)); Defensive patterns
Strategy: validation
Validate before calling
// reject bezier drawables with duplicate consecutive points before drawing
$points = iterator_to_array($bezier);
for ($i = 1, $n = count($points); $i < $n; $i++) {
$same = $points[$i]->x() === $points[$i - 1]->x()
&& $points[$i]->y() === $points[$i - 1]->y();
if ($same) {
throw new InvalidArgumentException('Duplicate consecutive bezier points');
}
}
$image->drawBezier($bezier); Type guard
use Intervention\Image\Geometry\Bezier;
function hasDistinctAdjacentPoints(Bezier $bezier): bool
{
$points = iterator_to_array($bezier);
for ($i = 1, $n = count($points); $i < $n; $i++) {
if ($points[$i]->x() === $points[$i - 1]->x()
&& $points[$i]->y() === $points[$i - 1]->y()) {
return false;
}
}
return true;
} Try / catch
use Intervention\Image\Exceptions\ModifierException;
try {
$image->drawBezier($bezier);
} catch (ModifierException $e) {
// degenerate curve: fall back to a thin border, which tolerates duplicates
$bezier->setBorder($bezier->borderColor(), 1);
$image->drawBezier($bezier);
} Prevention
- Never repeat identical coordinates in consecutive control points.
- When generating points programmatically, assert they change by at least one pixel.
- Prefer 1px borders for curves that may degenerate.
When it happens
Trigger: $image->drawBezier() with ->border($color, 2) or thicker where two consecutive control points are identical (e.g. first() equals second()); a cubic whose first three points coincide; a curve so small that consecutive 0.05-step interpolation samples round to the same integer pixel; all coordinates defaulting to (0, 0).
Common situations: Data-driven drawing where points come from variables that were never offset; reusing one Point instance for multiple slots; scaling vector overlays below 1px so all samples collapse.
Related errors
- Unable to read exif data, division by zero
- You must specify either 3 or 4 points to create a bezier cur
- The stroke color must be fully opaque
- Border size must be greater than or equal to 0
- Unable to parse RGB color from input "{input}"
AI-assisted analysis of Intervention/image@5598b9e397 (2026-08-23).
Data as JSON: /api/errors/4c6b2a3c3a25d12d.
Report an issue: GitHub.