Intervention/image · error · Intervention\Image\Exceptions\InvalidArgumentException

Invalid hsl() color syntax "{input}"

Error message

Invalid hsl() color syntax "{input}"

What it means

The HSL string decoder accepts 'hsl(...)' or 'hsla(...)' with a numeric hue (optionally suffixed 'deg'), saturation and luminance as plain numbers or percentages, and an optional alpha ('/ 0.5', comma or space separated, in 0-1 decimal or N% form). Strings that start with 'hsl' but violate this grammar throw the 'Invalid hsl() color syntax' InvalidArgumentException. Component values are matched as non-negative decimals, so negatives or letters fail here before any range validation happens.

Source

Thrown at src/Colors/Hsl/Decoders/StringColorDecoder.php:53

            return false;
        }

        if (!str_starts_with(strtolower($input), 'hsl')) {
            return false;
        }

        return true;
    }

    /**
     * Decode hsl color strings.
     *
     * @throws InvalidArgumentException
     */
    public function decode(mixed $input): ColorInterface
    {
        if (preg_match(self::PATTERN, $input, $matches) !== 1) {
            throw new InvalidArgumentException('Invalid hsl() color syntax "' . $input . '"');
        }

        $values = array_map(fn(string $value): int => match (strpos($value, '%')) {
            false => intval(trim($value)),
            default => intval(trim(str_replace('%', '', $value))),
        }, [$matches['h'], $matches['s'], $matches['l']]);

        // alpha value
        if (array_key_exists('a', $matches)) {
            $values[] = match (strpos($matches['a'], '%')) {
                false => floatval(trim($matches['a'])),
                default => floatval(trim(str_replace('%', '', $matches['a']))) / 100,
            };
        }

        return new Color(...$values);
    }
}

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Use canonical syntax: 'hsl(0, 100%, 50%)' or 'hsla(0, 100%, 50% / 0.5)'
  2. Validate with a regex or try/catch around parse before trusting user input
  3. Normalize input: trim whitespace, replace Unicode minus/quotes with ASCII equivalents
  4. Build programmatically when values are dynamic: Hsl\Color::create(0, 100, 50, 0.5)

Example fix

// before
$color = Hsl\Color::parse('hsla(-20, 100%, 50% / 50%)');

// after
$color = Hsl\Color::parse('hsla(340, 100%, 50% / 0.5)');
Defensive patterns

Strategy: validation

Validate before calling

if (!preg_match('/^hsla? ?\( ?[0-9.]+(deg)?(, ?| )[0-9.]+%?(, ?| )[0-9.]+%?( ?\/ ?|[, ] ?)?((0?\.[0-9]+)|1\.0|\.[0-9]+|[0-9]{1,3}%|1|0)? ?\)$/i', trim($input))) {
    // reject before Hsl\Color::parse()
}

Type guard

function isHslString(mixed $input): bool
{
    return is_string($input)
        && str_starts_with(strtolower(trim($input)), 'hsl')
        && str_contains($input, '(')
        && str_ends_with(trim($input), ')');
}

Try / catch

use Intervention\Image\Exceptions\InvalidArgumentException as ImageArg;

try {
    $color = Hsl\Color::parse($input);
} catch (ImageArg $e) {
    $color = Hsl\Color::create(0, 0, 0); // or re-prompt the user
}

Prevention

When it happens

Trigger: Hsl\Color::parse('hsl(-10, 50%, 50%)') (negative hue); 'hsl(0 100% 50' (missing ')'); 'hsl(0, 100%, 50% / 1.5)' (alpha grammar only allows 0-1 decimals or up-to-3-digit percents); 'hsl(360deg,100%,50%,)' (trailing comma); smart quotes/Unicode minus from copied text.

Common situations: Free-text color fields in CMS/forms; CSS strings pasted from design tools with alternate spacing or Unicode punctuation; dynamically assembled strings with missing delimiters.

Related errors


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