Intervention/image · error · ColorDecoderException

Result must be instance of {ColorInterface::class}

Error message

Result must be instance of {ColorInterface::class}

What it means

The color decoder chain finished but its result is not a ColorInterface instance. The driver validates the handler's result before returning it. All built-in color decoders return colors, so this indicates a custom color decoder in the chain violating the decoder contract.

Source

Thrown at src/Drivers/AbstractDriver.php:100

     * @throws ColorDecoderException
     * @throws DriverException
     */
    public function decodeColor(mixed $input, ?array $decoders = null): ColorInterface
    {
        $decoders = $decoders === null ? InputHandler::COLOR_DECODERS : $decoders;

        if (count($decoders) === 0) {
            throw new InvalidArgumentException('No decoders in array');
        }

        try {
            $result = InputHandler::usingDecoders($decoders, $this)->handle($input);
        } catch (NotSupportedException) {
            throw new ColorDecoderException('Unknown color format');
        }

        if (!$result instanceof ColorInterface) {
            throw new ColorDecoderException('Result must be instance of ' . ColorInterface::class);
        }

        return $result;
    }

    /**
     * {@inheritdoc}
     *
     * @see DriverInterface::specializeModifier()
     *
     * @throws NotSupportedException
     */
    public function specializeModifier(ModifierInterface $modifier): ModifierInterface
    {
        return $this->specialize($modifier);
    }

    /**

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Return a ColorInterface from the custom decoder - e.g. build and return an Intervention\Image\Colors\Rgb\Color
  2. Convert the custom format to Rgb\Color inside the decoder before returning

Example fix

// before
public function decode(mixed $input): mixed
{
    return $input; // raw token struct
}

// after
public function decode(mixed $input): ColorInterface
{
    return new RgbColor($input->r, $input->g, $input->b);
}
Defensive patterns

Strategy: type-guard

Type guard

function isValidColorDecoder(mixed $decoder): bool
{
    return $decoder instanceof \Intervention\Image\Interfaces\DecoderInterface;
}

Try / catch

try {
    $color = $driver->decodeColor($input);
} catch (ColorDecoderException $e) {
    // a registered custom color decoder returned a non-color - fix the decoder
}

Prevention

When it happens

Trigger: Registering a custom color decoder whose decode() returns a string, array, or DTO, then calling any color-accepting API that routes through $driver->decodeColor().

Common situations: Custom color decoders added to support app-internal color types (design tokens, serialized color structs) that return the raw struct instead of a ColorInterface.

Related errors


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