Skip to content

fileType option silently ignored in two scenarios — file returned in original format without warning #241

@adrielairaldo

Description

@adrielairaldo

Description

The fileType option is not honored in two scenarios, and in both cases the library returns the file in its original format without any error, warning, or indication that the conversion didn't happen. The caller has no way to know except by manually checking result.type.

Environment

  • browser-image-compression@2.0.2
  • Tested on iOS Safari (iOS 26.3) and Chrome (Windows)
  • React + Vite project

Scenario 1: File already under maxSizeMBfileType ignored entirely

When the input file is already smaller than maxSizeMB, the library short-circuits and returns the original file without applying fileType conversion.

const options = {
    maxSizeMB: 0.156,          // 160 KB
    fileType: 'image/webp',
    useWebWorker: true,
    alwaysKeepResolution: true,
    maxIteration: 50,
};

// Input: 82 KB PNG (already under 160 KB limit)
const result = await imageCompression(file, options);

console.log(result.type); // "image/png" — NOT "image/webp"
console.log(result.size); // 82458 — identical to input, no processing happened

Expected: The file should be converted to WebP even if compression isn't needed, since fileType was explicitly requested.

Actual: The original PNG is returned unchanged. No error, no warning.

This happens on all browsers, not just Safari.

Scenario 2: Safari/WebKit — WebWorker path fails to convert format silently

On Safari (all versions), the WebWorker-based compression path does not convert to fileType: 'image/webp' even when the file does need compression. The file is compressed (size reduced) but stays in the original format.

// Same options as above, but with a larger file that DOES need compression
const result = await imageCompression(largePngFile, options);

// Chrome: result.type = "image/webp", result.size = reduced ✓
// Safari: result.type = "image/png", result.size = reduced, but format NOT converted ✗

Retrying with useWebWorker: false produces the same result on Safari — because the library internally uses canvas.toBlob('image/webp'), which Safari does not support.

Root cause: Safari does not support WebP encoding from canvas

canvas.toBlob('image/webp') and canvas.toDataURL('image/webp') are not supported on any version of Safari (including the latest iOS 26). Safari silently falls back to PNG when WebP is requested.

This is confirmed by:

Impact

The combination of these two issues means any Safari user uploading a small image will receive the original file back, unmodified, even when the caller explicitly requested WebP conversion. There is zero indication this happened — no error, no console warning, no rejected promise.

In our production app, this caused unprocessed PNG files to reach the server when we expected WebP. We had to implement a multi-level fallback chain to detect and handle this:

  1. Try useWebWorker: true
  2. If result type doesn't match fileType, retry with useWebWorker: false
  3. If still doesn't match, fall back to direct canvas.toBlob with the correct format (PNG in Safari's case, since WebP is not possible)

Suggested improvements

  1. When fileType conversion is skipped (Scenario 1): At minimum, log a warning. Ideally, always honor fileType even when no compression is needed — the caller explicitly asked for format conversion.

  2. When fileType conversion fails (Scenario 2): Check the result's MIME type against the requested fileType. If they don't match, either:

    • Throw an error / reject the promise
    • Log a warning to console
    • Add a property to the result indicating the conversion failed (e.g., result._fileTypeConversionFailed = true)
  3. Document the Safari limitation: The README mentions fileType as a supported option but doesn't document that it depends on canvas.toBlob browser support and will silently fail on Safari for WebP.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions