BookStackApp/BookStack · error · PrettyException

Invalid non-image file type when streaming from storage

Error message

Invalid non-image file type when streaming from storage

What it means

A PrettyException (HTTP 415) is thrown by ImageService::streamImageFromStorageResponse when the Content-Type of the streamed storage response does not start with 'image/'. This is a safety check ensuring only actual image files are streamed inline from storage, guarding against serving stored non-image or mislabeled files.

Source

Thrown at app/Uploads/ImageService.php:380

        return false;
    }

    /**
     * For the given path, if existing, provide a response that will stream the image contents.
     */
    public function streamImageFromStorageResponse(string $imageType, string $path): StreamedResponse
    {
        $disk = $this->storage->getDisk($imageType);

        $stream = $disk->stream($path);
        $fileSize = $disk->size($path);
        $imageName = basename($path);
        $downloadResponseFactory = new DownloadResponseFactory(request());
        $response = $downloadResponseFactory->streamedInline($stream, $imageName, $fileSize);

        $contentType = $response->headers->get('Content-Type');
        if (!str_starts_with($contentType, 'image/')) {
            throw new PrettyException('Invalid non-image file type when streaming from storage', 415);
        }

        return $response;
    }

    /**
     * Check if the given image extension is supported by BookStack.
     * The extension must not be altered in this function. This check should provide a guarantee
     * that the provided extension is safe to use for the image to be saved.
     */
    public static function isExtensionSupported(string $extension): bool
    {
        return in_array(strtolower($extension), static::$supportedExtensions);
    }

    /**
     * Get all mime-types for images formats which BookStack supports.
     */

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Verify the file at the given path is a genuine image and re-upload it if corrupt or replaced
  2. Delete the bad image record/file and upload the correct image via the UI or API
  3. Check whether an intermediary/proxy is rewriting the Content-Type header
  4. Use file (Linux) or getimagesize() to confirm the stored file's actual type

Example fix

// before: manually copied files into uploads
$ cp /tmp/notes.txt storage/app/uploads/images/gallery/notes.png
// after: upload via API/UI so type is validated
$ curl -X POST /api/image-gallery -F 'file=@photo.png' -F 'name=photo'
Defensive patterns

Strategy: validation

Validate before calling

// verify before streaming
$mimeType = app('filesystem')->disk($diskName)->mimeType($path);
if ($mimeType === null || !str_starts_with($mimeType, 'image/')) {
    abort(415, 'Requested file is not an image');
}

Type guard

function isImageMime(?string $mime): bool
{
    return $mime !== null && str_starts_with($mime, 'image/');
}

Try / catch

try {
    return $imageService->streamImageFromStorageResponse($diskName, $path);
} catch (PrettyException $e) {
    abort(415, 'The requested file is not a valid image.');
}

Prevention

When it happens

Trigger: Calling streamImageFromStorageResponse($path) for a path whose stored file is not an image (wrong file stored under an image path), or where the mime-type detection returns a non-image type (e.g. application/octet-stream for unknown/corrupt files).

Common situations: Manually placed or renamed files in the uploads directory (e.g. a .txt renamed to .png) where the storage server reports a non-image content type; database image records pointing at files uploaded by other means; misconfigured storage proxies overriding Content-Type headers.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/f5415c06a373442f. Report an issue: GitHub.