getgrav/grav · error · RuntimeException

500

500

Error message

Not Implemented

What it means

MediaObject::createResponse() turns a media object into an HTTP response after applying medium actions (resize etc.). The source marks the limitation with a FIXME: the response path (filemtime/filesize/streaming with Content-Type, Last-Modified, ETag) is only implemented for ImageMedium instances. Any other medium (video, audio, PDF, or a media object whose file is missing) causes RuntimeException('Not Implemented', 500).

Source

Thrown at system/src/Grav/Framework/Media/MediaObject.php:125

     *
     * @param array $actions
     * @return Response
     */
    public function createResponse(array $actions): ResponseInterface
    {
        if (!isset($this->media)) {
            return $this->create404Response($actions);
        }

        $media = $this->media;

        if ($actions) {
            $media = $this->processMediaActions($media, $actions);
        }

        // FIXME: This only works for images
        if (!$media instanceof ImageMedium) {
            throw new \RuntimeException('Not Implemented', 500);
        }

        $filename = $media->path(false);
        $time = filemtime($filename);
        $size = filesize($filename);
        $body = fopen($filename, 'rb');
        $headers = [
            'Content-Type' => $media->get('mime'),
            'Last-Modified' => gmdate('D, d M Y H:i:s', $time) . ' GMT',
            'ETag' => sprintf('%x-%x', $size, $time)
        ];

        return new Response(200, $headers, $body);
    }

    /**
     * Process media actions
     *

View on GitHub (pinned to 6040efed04)

Solutions

  1. Before triggering the response path, branch on the medium type and only use createResponse() for images (instanceof ImageMedium / getMime starting with 'image/').
  2. For non-image files, serve the original file or link directly (e.g. $media->url()) instead of the actions/response pipeline.
  3. If actions like resize are needed for videos, use ffmpeg-based or plugin-provided pipelines rather than Grav's image medium actions.
  4. Ensure the referenced file actually exists in the media folder so $this->media is set; otherwise you get the 404 branch instead of a broken response.

Example fix

// before
$response = $mediaObject->createResponse($actions); // video -> RuntimeException('Not Implemented', 500)

// after
$media = $mediaObject->getMedia();
if ($media instanceof \Grav\Common\Page\Medium\ImageMedium) {
    $response = $mediaObject->createResponse($actions);
} else {
    $response = new \Grav\Framework\Response($media ? $media->url() : '/fallback-file.png', 302);
}
Defensive patterns

Strategy: type-guard

Validate before calling

$media = $mediaObject->getMedia();
if ($media instanceof \Grav\Common\Page\Medium\ImageMedium) {
    $response = $mediaObject->createResponse($actions);
} else {
    // non-image: serve original or redirect
    $response = $media ? $media->url() : null;
}

Type guard

function mediaSupportsActionResponse(\Grav\Framework\Media\MediaObjectInterface $object): bool
{
    $media = $object->getMedia();

    return $media instanceof \Grav\Common\Page\Medium\ImageMedium;
}

Try / catch

try {
    $response = $mediaObject->createResponse($actions);
} catch (\RuntimeException $e) {
    if ('Not Implemented' === $e->getMessage() && $e->getCode() === 500) {
        // non-image medium: fall back to original file URL or 404
        $response = $mediaObject->getMedia() ? $mediaObject->getMedia()->url() : '/404-not-found';
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Requesting a media URL that triggers actions/responses for a non-image file, e.g. a video or PDF passed through a route that calls createResponse(); applying image-style actions to a non-image medium and then rendering the response; the media field resolving to a missing file so $this->media is unset for that type of flow combined with action parameters.

Common situations: Theme/template code reusing image-handling logic (thumbnails, resize filters) for arbitrary uploaded files; users uploading videos/PDFs into a media field that the template feeds into the media response API; upgrading Grav where such calls previously fell through to another code path.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/9de4ee6ad7b45ab8. Report an issue: GitHub.