NEW319985
REGRESSION (Safari/iOS 26.5.x): XHR/fetch send an empty request body (Content-Length: 0) for a disk-backed File when the page is controlled by a service worker
https://bugs.webkit.org/show_bug.cgi?id=319985
Summary REGRESSION (Safari/iOS 26.5.x): XHR/fetch send an empty request body (Content...
Pavel
Reported 2026-07-22 07:36:18 PDT
Created attachment 480716 [details] minimal reproduction html Since Safari/iOS 26.5.x, sending a `File` object obtained from `<input type="file">` as a request body results in an **empty body with `Content-Length: 0`** reaching the server. The browser computes the Content-Length itself, so this is not a transport truncation, the WebKit fails to read the disk-backed blob while serializing the request and silently substitutes an empty payload. Key observations: 1. `XMLHttpRequest.send(file)` → server receives 0 bytes, request header `Content-Length: 0` 2. `fetch(url, { method: 'PUT', body: file })` → identical result (**not** XHR-specific) 3. `await file.arrayBuffer()` in the page **works** and returns all bytes (1,689,238 B in our test) 4. Sending an in-memory copy — `new Blob([await file.arrayBuffer()], { type: file.type })` — **works**; the server receives the complete body 5. `file.size` reports the correct value throughout 6. No redirect, no CORS preflight, no service-worker `respondWith` is involved in the failing request (same-origin PUT, verified via HAR: the PUT goes straight to the endpoint and gets the app's 422 size-mismatch response) So only the network-process side of blob resolution appears broken; page-process reads are fine. Environment: - iPhone 15 Pro Max & iPad Air (2022), iOS 26.5.2 (23F84), Safari (UA token `Version/26.5.2`) - Also reproduced by end users on macOS Safari 26.5 (shared WebKit) - Files picked from both the iOS photo picker (JPEG, ~1.7 MB and ~3.3 MB) and the Files app (PDF, 17 kB and 97 kB) — file type and size do not matter - **Regression:** the same flow worked on the previous iOS version installed on the same devices (confirmed via update A/B test by two independent users); first user reports began shortly after the 26.5 rollout Steps to reproduce: 1. Open the attached `repro.html` on an https origin in Safari on iOS 26.5.x 2. Pick any file via the file input (a photo from the library or a PDF from Files) 3. Read the on-page log: it PUTs the picked File via XHR and via fetch to https://httpbin.org/put (CORS-enabled echo) and prints the Content-Length the server received, then repeats the XHR PUT with an in-memory copy of the same bytes Expected results: All three uploads report the server receiving `Content-Length` equal to `file.size`. Actual results: - XHR with the picked File: server receives `Content-Length: 0` - fetch with the picked File: server receives `Content-Length: 0` - `file.arrayBuffer()`: reads all bytes successfully - XHR with the in-memory Blob copy: server receives the full body
Attachments
minimal reproduction html (1.14 KB, text/html)
2026-07-22 07:36 PDT, Pavel
no flags
reproduction (391 bytes, text/javascript)
2026-07-22 08:37 PDT, Pavel
no flags
reproduction.html (3.04 KB, text/html)
2026-07-22 08:37 PDT, Pavel
no flags
Reproduction example for loading from IndexedDB (7.43 KB, text/html)
2026-08-19 08:16 PDT, Tobias
no flags
Pavel
Comment 1 2026-07-22 08:36:10 PDT
Update: the bug requires a service-worker-controlled page. The service worker does not intercept the failing requests, its fetch listener never calls event.respondWith(). The controlled state alone triggers the bug. Affects both XMLHttpRequest and fetch. file.arrayBuffer() in the page reads the full file regardless, and sending new Blob([await file.arrayBuffer()]) works even under SW control, only network-process serialization of the disk-backed blob fails. I've replaced the original single-file test case with a two-file reproduction (repro.html + sw.js, attached). Steps: host both files in the same directory over https, open repro.html (the page registers sw.js and becomes controlled via clients.claim()), pick any file — steps 1–2 report the server receiving Content-Length: 0. Click "Switch to B side" (unregisters the SW) and repeat — everything passes. Real-world impact: file uploads are broken in Safari 26.5.x for any web app using a service worker (e.g. Angular ngsw apps; the symptom also matches upload failures reported on github.com in Safari 26).
Pavel
Comment 2 2026-07-22 08:37:02 PDT
Created attachment 480717 [details] reproduction
Pavel
Comment 3 2026-07-22 08:37:43 PDT
Created attachment 480718 [details] reproduction.html
Radar WebKit Bug Importer
Comment 4 2026-07-23 16:15:16 PDT
Per Arne Vollan
Comment 5 2026-07-27 09:30:25 PDT
I am not able to reproduce with WebKit ToT on macOS. Is this an issue on macOS as well?
Pavel
Comment 6 2026-07-27 09:46:17 PDT
Few users reported us that the problem is on macOS as well, but most reports are from iOS. I was unable to replicate it myself on the macOS, only on the iOS.
Per Arne Vollan
Comment 7 2026-07-27 09:49:03 PDT
This is the output I am seeing when testing WebKit ToT on macOS. The test case is hosted locally over http. === run start === page controlled by SW: YES picked: test2.txt, size=5, type=text/plain 0) control GET: HTTP 200 — echo reachable 1) XHR disk-backed File: server received Content-Length=5; upload progress 5/5 2) fetch disk-backed File: server received Content-Length=5 3) file.arrayBuffer() read 5 bytes in-page 4) XHR in-memory Blob copy: server received Content-Length=5; upload progress 5/5
Per Arne Vollan
Comment 8 2026-07-27 16:04:56 PDT
So far, I am not able to reproduce on iOS, either.
Per Arne Vollan
Comment 9 2026-07-28 06:29:08 PDT
This was tested on iOS 26.6.
Alexey Proskuryakov
Comment 10 2026-08-03 15:20:55 PDT
Should we track this as a dupe of bug 319396, or is this separate?
Pavel
Comment 11 2026-08-03 23:35:04 PDT
Hey, I believe the described problem is exactly the same as the one I reported in this case. I did not have time to provide better reproduction files yet, but I was able to replicate the problem with those already attached. It is possible that my original assumption was wrong and the root cause isn't related to the service worker, but caused by the problems described in the bug you mentioned.
Per Arne Vollan
Comment 12 2026-08-10 13:41:31 PDT
*** Bug 319396 has been marked as a duplicate of this bug. ***
Pavel
Comment 13 2026-08-14 06:34:12 PDT
Hello. As of today, users of our application faced this error on all latest iOS Safari versions. Based on the User-Agent header, we have confirmed incidents from iOS 26.6 and iOS 27. All reports have "AppleWebKit/605.1.15" as part of the User-Agent header.
Per Arne Vollan
Comment 14 2026-08-17 14:07:18 PDT
Hi! Unfortunately, I am still not able to reproduce with the attached test case. Could you also file a feedback report? This will provide us with more detailed information. === run start === page controlled by SW: YES picked: test.txt, size=5, type=text/plain 0) control GET: HTTP 200 — echo reachable 1) XHR disk-backed File: server received Content-Length=5; upload progress 5/5 2) fetch disk-backed File: server received Content-Length=5 3) file.arrayBuffer() read 5 bytes in-page 4) XHR in-memory Blob copy: server received Content-Length=5; upload progress 5/5
Tobias
Comment 15 2026-08-19 08:16:35 PDT
Created attachment 481111 [details] Reproduction example for loading from IndexedDB Hi, I have encountered what I believe to be a related bug. When saving a File instance to IndexedDB, loading it from IndexedDB and then sending that File Blob via FormData using fetch, no actual data is sent (Content-Length is 0) and the server encounters an unexpected EOF. It only happens with File objects not Blob objects in my reproduction example. I included a checkbox to test both cases. I have prefilled httpbun.com as a target server in the reproduction example, but any http server can probably be used. In my case it is not related to Service Workers and can be served from any http context. The bug could not be reproduced on macOS Tahoe 26.3.1 Noticed bug first on iOS and tested again on iPad (26.6) Steps to reproduce: 1. Serve html file on a server accessible to the iOS device 2. Access served html file on testing device 3. Press run button Expected behavior: In all 3 test cases that are run, the file should be sent to the server Actual behavior: In the second test case (loaded from IndexedDB) the Content-Length is 0 and no further data is sent. Server might respond with a 400 error (EOF).
Tobias
Comment 16 2026-08-19 09:29:08 PDT
After updating to macOS 26.6.2 I can confirm that the bug is now reproducible in macOS Safari as well. I also just realized that the tests can be run directly by opening the attachment link in the browser and clicking on the run button. No need to host separately.
Max
Comment 17 2026-08-25 02:49:33 PDT
Adding a data point that may help, since reproduction has been the blocker here: on an affected device the failure coincides exactly with a **sandbox denial in the networking process**, and I have a clean negative control for it. **Setup.** A Meteor web app whose page is service-worker-controlled, uploading a photo picked from the Photo Library via `multipart/form-data` (XHR) to a third-party storage API. Device: **iPhone 12, iOS 26.6**, Safari, rented from AWS Device Farm. Two builds of the same app, in one 19-minute session, same device, same photo: - **Build A (unfixed):** sends the picked `File` directly. Upload arrives at the server with an empty body. - **Build B (fixed):** sends `new Blob([await file.arrayBuffer()], {type})` instead — the workaround from comment #0. Build A failed both times it was tried; Build B succeeded both times. **The device log shows why.** Across 18,339 log entries for the session there are **exactly two** `file-issue-extension` denials, and they fall on the two Build A failures: ``` 16:13:47 Sandbox: com.apple.WebKit.Networking(609) deny(1) file-issue-extension target:/private/var/mobile/Containers/Data/Application/<UUID>/tmp/WKFileUploadPanel-JwERDyET/IMG_0001.jpeg extension-class:com.apple.app-sandbox.read 16:22:20 Sandbox: com.apple.WebKit.Networking(609) deny(1) file-issue-extension target:/private/var/mobile/Containers/Data/Application/<UUID>/tmp/WKFileUploadPanel-WqkWHVtW/IMG_0001.jpeg extension-class:com.apple.app-sandbox.read ``` There is **no such denial** at the two Build B successes. Timeline: | Time | Build | Result | `file-issue-extension` denial | | :------: | :---------: | :------------: | :---------------------------: | | 16:13:47 | A (unfixed) | **empty body** | **yes** | | 16:16 | B (fixed) | success | no | | 16:19 | B (fixed) | success | no | | 16:22:20 | A (unfixed) | **empty body** | **yes** | The target is the file being uploaded, staged in WebKit's own `WKFileUploadPanel` temporary directory, and the extension class is `com.apple.app-sandbox.read`. Read literally: **the networking process is refused read access to the file it is being asked to send**, which would produce exactly the observed `Content-Length: 0`. It also explains why the in-memory `Blob` workaround is effective — the bytes are read in the WebContent process, which does have access, so the networking process never needs a file extension at all. **Caveats, stated honestly:** - n=2 failures and n=2 successes. Perfect correlation, small sample. - The device is a **rented, supervised/managed device** in a device farm. Its sandbox profile may not be representative of a consumer device, and I cannot rule out that the denial is likelier there. That said, the fail/pass correlation is *within the same device and session*, so it does not depend on the device being typical. - I have not proven causation — only that the denial appears on the failures and not the successes, and that it names the right file at the right moment. - My log shows no service-worker involvement in the failing request itself, consistent with comment #1 (controlled state alone). Happy to provide the full session log if useful. I do not have a `sysdiagnose` — the device was rented by the minute and the session has ended — but if a sandbox-profile difference around `file-issue-extension` for `WKFileUploadPanel` paths is a plausible lead, that may be checkable in the source without a reproduction.
Note You need to log in before you can comment on or make changes to this bug.