{"record":{"id":"f5415c06a373442f","repo":"BookStackApp/BookStack","slug":"invalid-non-image-file-type-when-streaming-from-st","errorCode":null,"errorMessage":"Invalid non-image file type when streaming from storage","messagePattern":"Invalid non-image file type when streaming from storage","errorType":"http","errorClass":"PrettyException","httpStatus":415,"severity":"error","filePath":"app/Uploads/ImageService.php","lineNumber":380,"sourceCode":"        return false;\n    }\n\n    /**\n     * For the given path, if existing, provide a response that will stream the image contents.\n     */\n    public function streamImageFromStorageResponse(string $imageType, string $path): StreamedResponse\n    {\n        $disk = $this->storage->getDisk($imageType);\n\n        $stream = $disk->stream($path);\n        $fileSize = $disk->size($path);\n        $imageName = basename($path);\n        $downloadResponseFactory = new DownloadResponseFactory(request());\n        $response = $downloadResponseFactory->streamedInline($stream, $imageName, $fileSize);\n\n        $contentType = $response->headers->get('Content-Type');\n        if (!str_starts_with($contentType, 'image/')) {\n            throw new PrettyException('Invalid non-image file type when streaming from storage', 415);\n        }\n\n        return $response;\n    }\n\n    /**\n     * Check if the given image extension is supported by BookStack.\n     * The extension must not be altered in this function. This check should provide a guarantee\n     * that the provided extension is safe to use for the image to be saved.\n     */\n    public static function isExtensionSupported(string $extension): bool\n    {\n        return in_array(strtolower($extension), static::$supportedExtensions);\n    }\n\n    /**\n     * Get all mime-types for images formats which BookStack supports.\n     */","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Uploads/ImageService.php#L362-L398","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the file at the given path is a genuine image and re-upload it if corrupt or replaced","Delete the bad image record/file and upload the correct image via the UI or API","Check whether an intermediary/proxy is rewriting the Content-Type header","Use file (Linux) or getimagesize() to confirm the stored file's actual type"],"exampleFix":"// before: manually copied files into uploads\n$ cp /tmp/notes.txt storage/app/uploads/images/gallery/notes.png\n// after: upload via API/UI so type is validated\n$ curl -X POST /api/image-gallery -F 'file=@photo.png' -F 'name=photo'","handlingStrategy":"validation","validationCode":"// verify before streaming\n$mimeType = app('filesystem')->disk($diskName)->mimeType($path);\nif ($mimeType === null || !str_starts_with($mimeType, 'image/')) {\n    abort(415, 'Requested file is not an image');\n}","typeGuard":"function isImageMime(?string $mime): bool\n{\n    return $mime !== null && str_starts_with($mime, 'image/');\n}","tryCatchPattern":"try {\n    return $imageService->streamImageFromStorageResponse($diskName, $path);\n} catch (PrettyException $e) {\n    abort(415, 'The requested file is not a valid image.');\n}","preventionTips":["Only store validated image uploads under image paths (use the app's API/UI)","Audit manually copied files in the storage directory for renamed non-images","Do not place proxies/CDNs in front of storage that rewrite Content-Type headers","Check mime type with getimagesize()/file before serving stored files inline"],"tags":["php","laravel","http-415","content-type","image"],"backgroundTag":"unsupported-media-type","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}