BookStackApp/BookStack · warning · ImageUploadException
errors.image_upload_replace_type
Error message
errors.image_upload_replace_type
What it means
ImageUploadException with 'errors.image_upload_replace_type' is thrown by ImageRepo::updateImageFile when the extension of the replacement uploaded file does not match the extension of the existing image record's stored path. BookStack forbids replacing an image with a different file type (e.g. swapping a .png for a .jpg) to keep stored data consistent.
Source
Thrown at app/Uploads/ImageRepo.php:183
*/
public function updateImageDetails(Image $image, $updateDetails): Image
{
$image->fill($updateDetails);
$image->updated_by = user()->id;
$image->save();
$this->imageResizer->loadGalleryThumbnailsForImage($image, false);
return $image;
}
/**
* Update the image file of an existing image in the system.
* @throws ImageUploadException
*/
public function updateImageFile(Image $image, UploadedFile $file): void
{
if (strtolower($file->getClientOriginalExtension()) !== strtolower(pathinfo($image->path, PATHINFO_EXTENSION))) {
throw new ImageUploadException(trans('errors.image_upload_replace_type'));
}
$image->refresh();
$image->updated_by = user()->id;
$image->touch();
$image->save();
$this->imageService->replaceExistingFromUpload($image->path, $image->type, $file);
$this->imageResizer->loadGalleryThumbnailsForImage($image, true);
}
/**
* Destroys an Image object along with its revisions, files and thumbnails.
*
* @throws Exception
*/
public function destroyImage(?Image $image = null): void
{View on GitHub (pinned to 18f8469a1c)
Solutions
- Upload a replacement file with the same extension as the original image
- Alternatively delete the existing image and upload the new one as a fresh image
- Check $image->path extension first and convert/normalize the new file client-side before replacing
Example fix
// before
$repo->updateImageFile($image, $jpegFile); // image.path is foo.png
// after
if (strtolower($jpegFile->getClientOriginalExtension()) === strtolower(pathinfo($image->path, PATHINFO_EXTENSION))) {
$repo->updateImageFile($image, $jpegFile);
} else {
$repo->destroyImage($image);
$repo->saveNew($jpegFile, 'foo.jpg', $galleryId);
} Defensive patterns
Strategy: validation
Validate before calling
$existingExt = strtolower(pathinfo($image->path, PATHINFO_EXTENSION));
$newExt = strtolower($file->getClientOriginalExtension());
if ($existingExt !== $newExt) {
return back()->withErrors(["Replacement must be .$existingExt to match the original image."]);
} Try / catch
try {
$imageRepo->updateImageFile($image, $file);
} catch (ImageUploadException $e) {
return back()->withErrors(['image' => 'File type must match the original image extension.']);
} Prevention
- Compare the replacement file's extension with the existing image path's extension before calling updateImageFile
- Offer users a delete-then-upload flow instead of replace when formats differ
- Normalize/convert uploaded images to the original format server-side before replacing
- Restrict the file input's accept attribute to the original image's MIME type in the UI
When it happens
Trigger: Calling updateImageFile($image, $file) where strtolower($file->getClientOriginalExtension()) !== strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) — i.e. uploading a JPEG to replace a PNG record, or vice versa.
Common situations: Users replacing a gallery/cover image via the image manager UI with a different-format file; automated uploads using the API image replace endpoint with a differently-typed file; case mismatch is handled (lowercased) so only true type differences trigger it.
Related errors
- auth.registration_email_domain_invalid
- errors.path_not_writable
- auth.email_confirm_send_error
- Book to move chapter into not found
- Book or chapter to move page into not found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/145f57e22bbdec56.
Report an issue: GitHub.