Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[console/settings] 系统设置全部命名空间页白屏「Cannot convert undefined or null to object」——settings 客户端未跟进 framework#3843 响应信封(body.data) #3366

Closed

Description

@baozhoutao

现象

Console「系统设置 → 配置」下所有 settings 命名空间页(本地化/公司信息/品牌/认证/邮件/短信/存储/AI/知识库/功能开关/数据生命周期,共 11 个)打开即整页错误边界:

出错了 — Cannot convert undefined or null to object

「全部设置」列表页(/_console/apps/setup/system/settings)则显示假空态:「尚未注册任何设置。插件可通过 SettingsService 注册设置清单。」——而同一实例 GET /api/settings(带管理员会话)实际返回 11 个 manifests。

空库全新实例(os dev --fresh --seed-admin)即 100% 复现,与业务数据无关。

环境

  • @objectstack/* 17.0.0-rc.3(console 打包资产 SettingsView-HmY6cAH1.js)
  • 复现路径:Dev Admin 登录 → /_console/apps/setup/system/settings/localization(任一命名空间同现)

根因:settings 客户端未跟进 framework#3843 的响应信封

framework#3843(changeset 0931185,breaking)把 service-settings/settings-routes.ts 的全部 5 个响应体搬进声明信封 { success, data },并配了 envelope.conformance.test.ts + repo 级 check:route-envelope CI 守卫。其 changelog 明说:

Raw fetch callers must add one hop: body.data.

而 console 的 settings 客户端恰是 raw fetch 调用方,至今按信封前的旧形状直读(objectui main 68b6a2855 现行代码):

  • apps/console/src/pages/settings/api.ts:65 getSettingsNamespace() —— jsonOrThrow 直接 return res.json(),把整个信封当 SettingsNamespacePayload 返回;
  • apps/console/src/pages/settings/SettingsView.tsx:97 —— Object.entries(payload.values),信封顶层无 values ⇒ TypeError: Cannot convert undefined or null to object ⇒ 错误边界整页接管;
  • apps/console/src/pages/settings/api.ts:60 listSettingsManifests() —— 同病,列表页拿不到 manifests ⇒ 渲染成「尚未注册任何设置」的软失败(比白屏更迷惑:管理员会以为插件没注册);
  • apps/console/src/pages/settings/api.ts:73 saveSettingsNamespace()(PUT)—— 返回值同样期望顶层 { values },修好 GET 后保存回读也会踩同一坑,请一并处理;
  • 附带:invokeSettingsAction()(POST)对 {ok:boolean} 的探测因信封而失手,回落到 {ok: res.ok, message: statusText},动作能跑但 message 丢失——低优先,顺手可修。

#3843 的 changelog 备注过「objectui's packages readers were already tolerant」——packages 读者确实兼容了,但 settings 读者被漏掉了。

实测证据(rc.3)

GET /api/settings/localization 实际返回(截断):

{"success":true,"data":{"manifest":{"namespace":"localization",...},"values":{"timezone":{"value":"Asia/Shanghai","source":"env","locked":true,...},...}}}

浏览器崩溃栈:

TypeError: Cannot convert undefined or null to object
    at Object.entries (<anonymous>)
    at SettingsView-HmY6cAH1.js:1:8634   ← useMemo: Object.entries(payload.values)

影响

Setup 的整个「配置」板块在 17.0.0-rc.3 上对所有租户/所有管理员完全不可用(时区、语言、品牌、邮件投递、存储等全部设置项无法从 UI 查看或修改);列表页假空态还会把排查方向带偏到「插件没注册」。

建议修法

jsonOrThrow 处统一容错解包,兼容信封前后两代服务端(与 ObjectStackClient.unwrapResponse 同判定):

const body = await res.json();
return (body && typeof body.success === 'boolean' && 'data' in body) ? body.data : body;

发现自下游项目 os-project-titanwind-ehr#813(该项目登记号 PLAT-DEF-047)。

Activity

  1. self-assigned this
    on Aug 5, 2026
  2. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    Contributor

    认领:objectui 分片 PM 循环第 1 轮扩批(batch 3→5,维护者提速指令;v17,rc.3 回归)
    会话:session_01GTRjn8xBqp75dk7kFupVRt
    分支:claude/issue-3366-settings-envelope-unwrap
    Worktree:objectui-issue-3366
    域:objectui 分片(apps/console)
    文件面:apps/console/src/pages/settings/api.ts、apps/console/src/pages/settings/SettingsView.tsx + 同目录测试 + .changeset/*.md(越界即停,报告说明)

    前提已对 origin/main @ 68b6a28 核验:api.ts:23 仍 return res.json() 直读、SettingsView.tsx:97 仍 Object.entries(payload.values),信封适配确实缺失。修法口径:按正文建议在 jsonOrThrow 统一解包,与 ObjectStackClient.unwrapResponse 同判定,兼容信封前后两代服务端;GET/PUT/action 三面一并处理。


    Generated by Claude Code

  3. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    Contributor

    PM 验收:ACCEPT → PR #3372 已转 ready + auto-merge(报告时 11 项完成全绿,4 个 test 分片在跑,零失败;绿齐自动入队),跟到 MERGED。

    落地内容:生产代码只动 apps/console/src/pages/settings/api.ts 一个文件 —— ① jsonOrThrow 统一解包,判定与 ObjectStackClient.unwrapResponse 逐字相同(success 为 boolean 且存在 data,错误信封因无 data 刻意不解包,err.payload.error 继续喂 objectstack#4224 的 locked-key/逐字段渲染);② action 端点两臂各读各的(成功臂 data、上报失败臂 error.details),message/severity 不再丢;③ 各端点断言承诺形状,读不了抛具名 Malformed response,坏 body 不再冒充好 body 走成白屏或假空态。19 例单元 + 5 例双屏集成(走真 ./api + stub fetch);反向验证按预判方向 8 红 25 绿,且还原到修复前状态时集成测试复现 issue 里逐字相同的崩溃栈 —— 测试钉的是真 bug。双代服务端兼容分支被测试逐条钉名,将来删旧分支会是一条点名的红测试,不是静默回归。

    顺带核对:console 另两处 raw fetch 直读(/auth/me/localization、/forms/:slug)服务端本就返回裸 body,不属同族,无需改动。

    v17 标记单(rc.3 回归,系统设置板块整体恢复)至此代码侧闭环,待队列落地。

    objectui 分片 PM session_01GTRjn8xBqp75dk7kFupVRt


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:dispatchedpm:queuetarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions