Summary
On Safari (tested 17.5 and 26.4 on macOS), the admin edit view's autosave hook
emits zero PATCH /api/<collection>/:id?autosave=true requests while the
user types in localized fields. The form does emit Server-Action POST /admin/collections/<collection>/:id requests that return 200 with a
~800-byte body, so the UI shows a "last saved Xs ago" indicator and no error.
But the underlying _<collection>_v_locales.version_title (and other
localized fields) stay empty in the database. Editors believe their work is
saved; on reload, everything is gone.
The same Payload bundle, same user, same document, in Chrome works correctly:
PATCH ?autosave=true fires every ~1s and the draft persists.
Environment
payload@3.84.1
@payloadcms/next@3.84.1
@payloadcms/db-postgres@3.84.1
@payloadcms/richtext-lexical@3.84.1
- Next.js
15.4.11, Node 22, Postgres 16
- Reproduced on:
- Safari 17.5 / macOS
- Safari 26.4 / macOS (latest)
- Working on: Chrome 147 / macOS
Collection config (relevant)
{
slug: "posts",
versions: { drafts: { autosave: { interval: 800 }, schedulePublish: true } },
access: {
read: ({ req: { user } }) => !!user || ({ status: { equals: "published" } } as never),
},
// localized fields: title, excerpt, content (lexical)
}
// localization: { locales: [fr, de], defaultLocale: "fr", fallback: true }
Reproduction
- Open the Payload admin in Safari (any version 17+), authenticated.
- Create a new post in a draft+autosave-enabled collection with localized
fields. Stay on the default locale.
- Type a title. Wait the autosave debounce window (we use 800 ms).
- Watch the network tab and the database.
Expected: PATCH /api/posts/:id?autosave=true&...&locale=fr fires, DB
gets a new version row with the title.
Actual: zero PATCH requests. Only POST /admin/collections/posts/:id
requests fire (Server Action). Returns 200. DB row in _posts_v_locales
stays empty.
Evidence — same user, same document, two browsers
Server access log for the same authenticated user on the same post,
back-to-back sessions:
# Safari 26.4 (n=3 typing events)
POST /cms/admin/collections/posts/851 → 200 (body 832B)
POST /cms/admin/collections/posts/851 → 200 (body 814B)
POST /cms/admin/collections/posts/851 → 200 (body 822B)
# Zero PATCH /cms/api/posts/851?autosave=true
# Chrome 147 (same role, same doc, n=4 typing events)
POST /cms/admin/collections/posts/850 → 200
PATCH /cms/api/posts/850?autosave=true&...&locale=fr → 200 ✓ persists
POST /cms/admin/collections/posts/850 → 200
PATCH /cms/api/posts/850?autosave=true&...&locale=fr → 200 ✓ persists
DB after the Safari session:
SELECT v._locale, v.version_title FROM _posts_v_locales v
JOIN _posts_v p ON v._parent_id = p.id WHERE p.parent_id = 851 AND p.latest;
_locale | version_title
---------+---------------
fr |
de |
DB after the Chrome session (same user role, same flow):
_locale | version_title
---------+----------------------
fr | EDITOR FR TEST
de | Titre TEST admin v2
Ruled out
- Role/access: tested with
editor role in Chrome — autosave fires
correctly and persists. Verified /api/<col>/access/:id returns
{update: true, ...} for both admin and editor.
- Custom auth strategy: same custom auth strategy (validating a
session cookie via payload.auth({headers})) used in Chrome and Safari
sessions; resolves the user identically.
- CORS / WAF / upstream proxy: zero
403/blocked requests in our
edge or origin gateway logs during the Safari session.
- Document lock:
payload_locked_documents table empty for the doc.
- Collection access functions: defaults to
Boolean(user) for
update/create — no custom rule in play.
Hypothesis
Something in the autosave hook chain (useDebounce / useDeferredValue /
useEffectEvent / queueTask) or in requests.patch() (which is plain
fetch(url, {method: "PATCH", credentials: "include", body: formData}))
is silently no-op'ing on Safari/WebKit. The fact that the Server-Action
POST path still fires suggests the form itself is functional — only the
client-side autosave PATCH submit is dropped.
We have not yet identified whether the fetch call throws, the hook
never runs, or requests.patch resolves without a network round-trip.
Happy to attach Safari Web Inspector traces if useful — let me know what
would help triage.
Workaround
Hard banner at the top of every editable doc, detecting WebKit-non-Chromium
UA, telling editors to use Chrome/Firefox until a fix lands. Better than
silent data loss but obviously not a fix.
Impact
Two editors lost their work before we identified the cause; our prior
"fixes" (validated only in Chrome) gave false confidence. This is
effectively silent data corruption for any editor on Safari — a common
browser on macOS.
Summary
On Safari (tested 17.5 and 26.4 on macOS), the admin edit view's autosave hook
emits zero
PATCH /api/<collection>/:id?autosave=truerequests while theuser types in localized fields. The form does emit Server-Action
POST /admin/collections/<collection>/:idrequests that return200with a~800-byte body, so the UI shows a "last saved Xs ago" indicator and no error.
But the underlying
_<collection>_v_locales.version_title(and otherlocalized fields) stay empty in the database. Editors believe their work is
saved; on reload, everything is gone.
The same Payload bundle, same user, same document, in Chrome works correctly:
PATCH
?autosave=truefires every ~1s and the draft persists.Environment
payload@3.84.1@payloadcms/next@3.84.1@payloadcms/db-postgres@3.84.1@payloadcms/richtext-lexical@3.84.115.4.11, Node 22, Postgres 16Collection config (relevant)
Reproduction
fields. Stay on the default locale.
Expected:
PATCH /api/posts/:id?autosave=true&...&locale=frfires, DBgets a new version row with the title.
Actual: zero PATCH requests. Only
POST /admin/collections/posts/:idrequests fire (Server Action). Returns
200. DB row in_posts_v_localesstays empty.
Evidence — same user, same document, two browsers
Server access log for the same authenticated user on the same post,
back-to-back sessions:
DB after the Safari session:
DB after the Chrome session (same user role, same flow):
Ruled out
editorrole in Chrome — autosave firescorrectly and persists. Verified
/api/<col>/access/:idreturns{update: true, ...}for bothadminandeditor.session cookie via
payload.auth({headers})) used in Chrome and Safarisessions; resolves the user identically.
403/blocked requests in ouredge or origin gateway logs during the Safari session.
payload_locked_documentstable empty for the doc.Boolean(user)forupdate/create— no custom rule in play.Hypothesis
Something in the autosave hook chain (
useDebounce/useDeferredValue/useEffectEvent/queueTask) or inrequests.patch()(which is plainfetch(url, {method: "PATCH", credentials: "include", body: formData}))is silently no-op'ing on Safari/WebKit. The fact that the Server-Action
POST path still fires suggests the form itself is functional — only the
client-side autosave PATCH submit is dropped.
We have not yet identified whether the
fetchcall throws, the hooknever runs, or
requests.patchresolves without a network round-trip.Happy to attach Safari Web Inspector traces if useful — let me know what
would help triage.
Workaround
Hard banner at the top of every editable doc, detecting WebKit-non-Chromium
UA, telling editors to use Chrome/Firefox until a fix lands. Better than
silent data loss but obviously not a fix.
Impact
Two editors lost their work before we identified the cause; our prior
"fixes" (validated only in Chrome) gave false confidence. This is
effectively silent data corruption for any editor on Safari — a common
browser on macOS.