Intervention/image · error · StateException

No font file specified

Error message

No font file specified

What it means

toImagickDraw() throws StateException 'No font file specified' when the Font object has no file attached (src/Drivers/Imagick/FontProcessor.php:61-63). The Imagick driver has no built-in fallback font, so every text() or boxSize() call must supply a font file via the font closure. This is a pure usage error thrown before any ImageMagick call — nothing environmental.

Source

Thrown at src/Drivers/Imagick/FontProcessor.php:62

        return new Size(
            intval(round($dimensions['textWidth'])),
            intval(round($dimensions['ascender'] + $dimensions['descender'])),
        );
    }

    /**
     * Imagick::annotateImage() needs an ImagickDraw object - this method takes
     * the font object as the base and adds an optional passed color to the new
     * ImagickDraw object.
     *
     * @throws StateException
     * @throws DriverException
     */
    public function toImagickDraw(FontInterface $font, ?ImagickPixel $color = null): ImagickDraw
    {
        if (!$font->hasFile()) {
            throw new StateException('No font file specified');
        }

        try {
            $draw = new ImagickDraw();
            $draw->setStrokeAntialias(true);
            $draw->setTextAntialias(true);
            $draw->setFont($font->filepath());
            $draw->setFontSize($this->nativeFontSize($font));
            $draw->setTextAlignment(Imagick::ALIGN_LEFT);

            if ($color instanceof ImagickPixel) {
                $draw->setFillColor($color);
            }
        } catch (ImagickException | ImagickDrawException $e) {
            throw new DriverException('Failed to convert font to ImagickDraw instance', previous: $e);
        }

        return $draw;

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Add ->file($path) inside the font closure of your text() call
  2. Use an absolute path to a font you ship with the app (e.g. base_path('fonts/Inter-Regular.ttf'))
  3. Verify at runtime: is_readable($path) before calling text()
  4. Do not rely on system font paths that differ between dev, CI, and production

Example fix

// before
$image->text('Hello', 100, 100, function ($font) {
    $font->size(24);
});

// after
$image->text('Hello', 100, 100, function ($font) {
    $font->file(base_path('fonts/Inter-Regular.ttf'));
    $font->size(24);
});
Defensive patterns

Strategy: validation

Validate before calling

$fontFile = realpath(__DIR__ . '/fonts/Inter-Regular.ttf');
if ($fontFile === false || !is_readable($fontFile)) {
    throw new RuntimeException('Font file missing: ' . $fontFile);
}
// ->file() is what prevents the StateException
$image->text('Hello', 100, 100, fn ($font) => $font->file($fontFile));

Type guard

// If you build Font objects yourself
if (!method_exists($font, 'hasFile') || !$font->hasFile()) {
    throw new RuntimeException('Font has no file; Imagick driver requires one');
}

Try / catch

use Intervention\Image\Exceptions\StateException;

try {
    $image->text($label, $x, $y, fn ($font) => $font->file($this->fontPath)->size(20));
} catch (StateException $e) {
    // usage bug: font file not set — fix the closure, do not retry
    throw new LogicException('text() requires ->file() on the Imagick driver', 0, $e);
}

Prevention

When it happens

Trigger: Calling $image->text('Hello', $x, $y, fn ($font) => $font->size(24)->color('f00')) without ->file(...); building an Intervention\Image\Typography\Font manually and never calling setFilepath() before boxSize()/toImagickDraw().

Common situations: Copy-pasting GD examples or Intervention Image v2 snippets where a default font existed; assuming a system default font is used; the font file argument simply forgotten during refactoring.

Related errors


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