Skip to content

sys_user_preference update reports droppedFields:[id] for a key the caller never sent — every org switch pops a user-facing warning toast #8093

Description

@baozhoutao

Switching the active organization (and, more generally, visiting any object page) pops a user-facing amber warning toast:

已保存,但部分字段未生效
以下字段为只读,未生效: 偏好设置 ID

Nothing the user edited was dropped. The write is the console's internal "recent items" trace.

Actual

The trigger is the ui.recent preference write. Response body of the PATCH behind the toast:

PATCH /api/v1/data/sys_user_preference/4mekbFDEhx0QgC85 → 200
{
  "object": "sys_user_preference",
  "id": "4mekbFDEhx0QgC85",
  "record": { "id": "4mekbFDEhx0QgC85", ..., "key": "ui.recent", "value": [ ... ] },
  "droppedFields": [ { "object": "sys_user_preference", "fields": ["id"], "reason": "readonly" } ]
}

The dropped field is id — the record's own primary key, carried in the URL path, and the client's update body does not contain it. ObjectStackDataSource.update() forwards exactly the data argument (packages/data-objectstack/src/index.ts:1573), and the caller passes only { value: items } (packages/data-objectstack/src/userState.ts:169dataSource.update(resource, cachedRowId, { value: items })). So the server appears to fold the path id into the candidate write set, judge it read-only, and then report it as a dropped field.

(The request body was not captured on the wire; the above is read off the client source in the current main checkout. If the running build does send id, this is a client bug instead — either way the toast is wrong.)

Expected

droppedFields reports fields the caller supplied and the engine refused. The primary key addressing the row is not a supplied field, and an internal preference write must never surface a warning to the user.

Both invariants are already written down:

This is that same drowning, one field over, and it is the loudest thing a user sees after switching workspaces.

Suggested fix

Server side: exclude the path/primary key from the read-only drop accounting on single-record update (it is addressing, not payload). Optionally also let a write opt out of drop reporting for internal preference traces.

Environment

Local dev server http://localhost:8080, multi-org enabled, zh locale, observed 2026-08-12 (reproduced on every org switch). The server was started by the maintainer and its exact commit is not verified.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions