Safari Uploads an Empty File? Check Service Worker Control

Your page reads the selected file and reports the right size, yet the server gets an empty body from Safari. What does a service worker have to do with it?

Safari Uploads an Empty File? Check Service Worker Control
TLDR
  • A valid File.size does not establish that its contents reached the server.
  • Record service worker control and where the File came from.
  • Technology Preview 255’s fix does not establish deployment to every stable Safari build.

Compare the selected file with the received payload

If Safari sends an empty upload, check the page’s service worker state before changing server limits. Technology Preview 255 lists a fix for disk-backed File bodies disappearing from fetch and XMLHttpRequest uploads on controlled pages.

The controller property identifies the worker controlling the current page. That is more specific than asking whether the application has ever registered one. A console check is:

console.log(navigator.serviceWorker?.controller?.scriptURL ?? null);

A script URL identifies the controller; it does not prove the worker caused the failure. Record it alongside the selected file’s size and the server’s received-byte count.

A null controller does not rule out file-handling failures

Bug 319985 includes reports of readable files producing empty uploads. Later reports describe Files restored from IndexedDB without a service worker, so keep the file’s origin in the investigation.

The fix’s commit message describes both a check blocking files in the initiating process’s IndexedDB data directory and an iOS sandbox issue with file-read extensions for the service worker process. Changing fetch to XHR is not an established solution.

Change one condition at a time

Use the failing request as a baseline. arrayBuffer() reads the contents into a binary buffer, allowing a separate check that the page can read the bytes. It does not test delivery.

RecordWhat it distinguishes
Picker, drop or IndexedDB originThe route by which the File entered the upload flow.
Current controllerWhether this page is controlled.
File.size and the buffer’s byteLengthAvailability of the contents before transmission.
Received request and file-part sizesActual delivery, independent of the page’s file object.

Keep the application’s request format when comparing cases. A multipart body includes boundaries and fields, so its total size is not expected to equal the file size. Compare the received file part separately where possible.

Test a memory-copy workaround at the intended file size

A reporter found that sending new Blob([await file.arrayBuffer()], { type: file.type }) worked around the problem. It reads the file into memory, making file size part of the decision to adopt it.

Technology Preview is a macOS test browser. Its release note is not proof that an iPhone or a stable Safari installation contains the fix. Close the production issue when the affected environment delivers the complete file, rather than when the picker shows a plausible size.