Skip to content

Console renders a datetime action param as zone-less datetime-local and posts it raw — the runtime validator then rejects every UI submission with 400 #5061

Description

@os-zhuang

在 hotcrm 的 dogfood 浏览器验证(objectstack-ai/hotcrm#592 / PR objectstack-ai/hotcrm#670,17.0.0-rc.2)中实测发现;对任何声明 type: 'datetime' 参数的 action 均成立,与具体 app 无关。

症状

action 参数声明为 type: 'datetime' 时,Console 把它渲染成 <input type="datetime-local">(其值天然无时区),提交时原样 POST:

POST /api/v1/actions/crm_opportunity/schedule_meeting
{"recordId":"cUwJQOIREpOsdccQ","params":{"subject":"…","start":"2026-08-10T15:00","duration":45}}

→ HTTP 400
{"code":"VALIDATION_ERROR","message":"Invalid action params: Action param \"start\" (datetime):
 expected an ISO-8601 instant with explicit zone (e.g. 2026-03-15T14:30:00.000Z)"}

没有任何用户可输入的值能通过校验 —— 渲染器产出的形状与校验器要求的形状不相交。用户看到一条错误 toast,对话框关闭,零记录写入。已在两个不同入口复现(list-view 行菜单与 record header),同一 400。

为什么这是平台缺陷而不是 app 缺陷

  • 渲染 datetime-local 的是 Console 的参数表单生成器;
  • 拒绝其输出的是 runtime 的 action-param 校验器(要求带时区的 ISO instant);
  • 两者都是平台组件,app 作者在两者之间没有任何可作用的缝隙 —— 声明一个 datetime 参数就注定该 action 在 UI 上不可用。这是「声明即强制」的反面教材:平台允许声明一个自己 UI 无法履约的参数类型。

两个候选修法(择一,建议前者)

  1. Console 侧:提交前把 datetime-local 的值按用户/浏览器时区序列化为带时区的 ISO instant(2026-08-10T15:002026-08-10T15:00:00.000+08:00 或 UTC 换算)。语义最正确,一次修复惠及所有 app。
  2. Runtime 侧:校验器接受无时区形状并按组织时区归一化。较宽松,但把「时区在哪」变成隐式约定,不建议。

影响与临时绕行

受影响面 = 所有带 datetime 参数的 action 的全部 UI 提交路径。hotcrm 侧的临时绕行(在 objectstack-ai/hotcrm#670 的返工里落地)是把 action 参数改为可被 Console 实际产出的形状并在 body 内归一化 —— 平台修复落地后应回退该绕行。

环境:17.0.0-rc.2,objectstack dev,Chromium(Playwright)。完整截图与网络记录见 hotcrm PR #670 的 dogfood 记录。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions