fix(settings): report a failed settings write instead of reporting success - #7023
Conversation
…ccess
The PATCH catch answered `{ success: true }` with 200, so a failed upsert was
indistinguishable from a saved one.
`useUpdateGeneralSetting` is optimistic: `onMutate` writes the new value into
the cache and calls `syncThemeToNextThemes`, and `onError` restores the previous
settings. `requestJson` only throws on a non-2xx, so `onError` could never run —
the rollback and its theme re-sync were unreachable code. A user toggling a
consent-shaped setting (telemetry, email opt-out) saw it applied and it was not
saved, until a later refetch quietly reverted it.
The catch now returns 500, which is what the mutation was already written to
handle.
Left alone deliberately: GET still falls back to `defaultUserSettings` on error.
Failing it would take the settings page down on a transient read, and the value
of changing it is a separate judgement from this one.
Covered by a route test that drives the failure through the real handler.
Verified it fails when the 200 is put back.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview The catch now returns 500 with an error body. That lets the existing optimistic Adds a route test that asserts success on a real write and failure when the insert throws. GET still returns defaults on error; that path is unchanged. Reviewed by Cursor Bugbot for commit c4b6c13. Configure here. |
Greptile SummaryThe PR corrects the settings PATCH endpoint so failed database writes return HTTP 500, allowing existing optimistic client mutations to execute their rollback behavior.
Confidence Score: 5/5The PR appears safe to merge because failed settings writes now reach the client’s existing error and rollback path without introducing a conflicting response contract. The route wrapper preserves the returned 500 response,
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/users/me/settings/route.ts | Correctly returns a non-success status when a settings upsert throws, matching the existing client error and rollback contract. |
| apps/sim/app/api/users/me/settings/route.test.ts | Adds correctly wired regression tests proving successful writes return 200 and thrown database writes return 500. |
Reviews (1): Last reviewed commit: "fix(settings): report a failed settings ..." | Re-trigger Greptile
The route cannot fail
app/api/users/me/settings/route.ts:75A failed upsert is answered exactly like a successful one.
Which makes the client's rollback unreachable
useUpdateGeneralSetting(hooks/queries/general-settings.ts:166) is a full optimistic mutation:onMutatewrites the new value into the cache and callssyncThemeToNextThemesonErrorrestorescontext.previousSettingsand re-syncs the themerequestJsonthrowsApiClientErroronly on a non-2xx. Since the route always answered 200,onErrorcould never run — the rollback and its theme re-sync are dead code.What the user sees
The upsert throws (pool exhaustion, a conflict on
settings.userId, a replica failover). The UI applies the change. Nothing is persisted.onSettledinvalidates, the refetch returns the old value, and the setting quietly reverts with no error anywhere.For a consent-shaped setting — telemetry, email opt-out — the user believes they opted out and did not.
The fix
The catch returns 500, which is what the mutation was already written to handle. No client change needed.
Deliberately not changed
GETstill falls back todefaultUserSettingson error, also at 200. Failing it would take the settings page down on a transient read, and whether that trade is right is a separate judgement from this one. Worth noting it compounds the above — a failing GET makes the post-write refetch show defaults — but the write path is the one making a false claim.Testing
A route test drives the failure through the real handler rather than asserting on a mock:
Verified it can fail — restoring the
200turns the second case red withexpected 200 to be 500.bun run type-checkclean; 41 tests passing acrossapp/api/usersand the settings hook.