console: changing the command-palette query after record hits have loaded blanks the whole console (cmdk "Cannot read properties of undefined (reading 'forEach')") #13387
Description
Requested by 田騏 · project thread
Found during an exploratory walkthrough of the showcase app.
Environment
- objectui
main@6fb31dc, console served bypnpm --filter @object-ui/console dev(:5180) - objectstack
main@b9c7a8f,PORT=3000 pnpm dev(showcase), signed in as the dev admin, locale zh-CN - Chromium (Playwright), 1440×900
Steps
- Open any app page, e.g.
/apps/com.example.showcase/showcase_task. - Press Ctrl/⌘+K and type
Website. Wait for the record hits (a 项目 group with Website Relaunch). - Change the query to something that returns a different set of record groups, either by select-all + typing
华宁/Co, or by deleting the text with Backspace and typingContoso.
Expected
The palette shows the hits for the new query.
Actual
The palette disappears and the entire console unmounts to an empty page (no shell, no sidebar); only a reload recovers. The page error is:
TypeError: Cannot read properties of undefined (reading 'forEach')
at .../node_modules/.vite/deps/cmdk.js:139:6
at Set.forEach (<anonymous>)
at z (.../cmdk.js:137:29)
at .../cmdk.js:106:5
at .../cmdk.js:483:28
at Map.forEach (<anonymous>)
...commitHookEffectListMount (react-dom_client.js)
followed by React's "An error occurred in the component. Consider adding an error boundary…".
Reproduced on every attempt (2/2 for each of the sequences above). A fresh query typed into a newly opened palette works (华宁, hn, Contoso, Data, 设置 all fine), and so does extending a query (Web → Website). It is the switch from one record-group set to another that crashes.
Reading of the cause (from the stack and the bundled cmdk 1.1.1 source, not debugged further)
cmdk's sort step (z in the minified build) walks state.filtered.groups and, for each id, reads that group's item set from its group map and calls .forEach on it. A CommandGroup that unmounts deletes its own map entry. In CommandPalette.tsx the record hits are rendered as one CommandGroup per object, keyed records:<objectName>, so when the new query stops returning an object that group unmounts while filtered.groups still names it, and the sort scheduled by an item's value change runs before the filter is recomputed. The lookup returns undefined and throws inside a layout effect.
Two separate things make it worse than a broken palette:
- nothing above the palette catches the error, so one bad search takes down the whole console;
- the record-group set is exactly the part of the palette that changes on every query, so ordinary use hits it.
Suggested direction
Make the record groups stable across queries (or let the async record hits bypass cmdk's own sort/filter, since their order and membership already come from /api/v1/search), and put an error boundary around the palette so a palette failure cannot unmount the shell.
Activity
marchtian commentedon Oct 23, 2026
Not a regression: this also reproduces on the released 18.0.0 console.
I reproduced it on the prebuilt
@objectstack/console@18.0.0from npm. Itsdist/.objectui-shais616df6598b429d2b3687301a2041198928e68971. I served it same-origin, in front of the same objectstackmainshowcase backend, and signed in as the dev admin. Both sequences in the issue crash it the same way (2/2), with the samepageerror: Cannot read properties of undefined (reading 'forEach').Between
616df6598and6fb31dcc9:CommandPalette.tsxis unchanged. The only edits there are theonSelecthandlers moving toconfirmUnsavedLeave(fix(app-shell): the command palette, the inbox and the AI entry ask before dropping unsaved edits (objectui#13253) #13265).packages/components/src/ui/command.tsxis unchanged.cmdk@1.1.1in both.The per-object record groups (
key={records:${group.objectName}}) were added in 8ecf5a6 (#2772, 2026-07-21), when the palette started showing/api/v1/searchhits. That is most likely where the crash comes from. I have not bisected it build by build.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Triage: lands in
apps/consoleCommandPalette.tsx(the per-objectrecords:<objectName>CommandGroups) plus an error boundary around the palette in the shell; rationale: ordinary use of ⌘K (changing the query) unmounts the whole console. Not a regression: the filer reproduced it on the released 18.0.0 console (.objectui-sha616df65), most likely since #2772 added per-object record groups. Two parts: make the async record groups bypass cmdk's own sort/filter (or keep them stable across queries), and contain any palette failure so it cannot take the shell down.Path: records · 搜得到…全局搜索与命令面板同一条路 | search.console-global-search (P1, fails on a changed query) | step ②
Grade:
priority:p1— inherits the P1 item, which fails; a whole-app white screen on a common action. Not P0: a reload recovers and nothing is lost. Release: it already ships in 18.0.0, so it does not make 18.1.0 worse; whether it holds the 18.1.0 cut is the maintainer's call, raised in the project thread.Type: Bug
Session:
session_01AkmasAfXt3cu83xGzkuKhe(objectstack triage, run on the maintainer's request in the project thread)Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Claim: project thread seat (maintainer-dispatched) at 2026-10-23T17:20Z
Session:
session_01Qfcwdm3pMxUcQjYXXJxRNtAccount:
marchtian(the seat's linked user; the card's assignee)Branch:
claude/project-thread-zt8k65Worktree:
objectui-13387Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/app-shell/src/chrome/(CommandPalette.tsxplus one new test file beside it), one.changeset/*.mdContainer & model:
S,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate),effort: xhigh (p1)Clause-②: no
Metadata-protocol: none
Responsibility:
objectui app-shell CommandPalette: per-object record CommandGroups fed to cmdk's own sort/filter|none: nothing above the palette catches a render-phase or layout-effect error|every console user who changes a ⌘K query after record hits load; ordinary use todayThread-read: 6370061042
Serial constraints cleared: none (no open PR touches
CommandPalette.tsxor the palette; searchedrepo:objectstack-ai/objectui is:open CommandPaletteandpalette)Dispatch provenance: the maintainer (田騏) ruled on the release decision card in the project thread, 2026-10-23T17:15Z, verbatim: 「不挡,现在就派人修」. This card does not block 18.1.0 and is worked now.
Plan, as the triage comment scopes it:
matchesPaletteQuery), and record hits come ranked from/api/v1/search, so cmdk's own filter and sort are turned off (shouldFilter={false}). The crash is cmdk's sort walking a group id that an unmounted per-object group has already removed; with its filter off that sort never runs.Generated by Claude Code
fix(app-shell): changing the ⌘K query no longer unmounts the console …
objectstack-fleet commentedon Oct 23, 2026
Ruled (maintainer, 2026-10-23, decision card in the project thread, option A verbatim): 「不挡,现在就派人修」 — do not hold 18.1.0 for this card; dispatch it now.
target:blocker. It already ships in 18.0.0 (.objectui-sha616df65), so 18.1.0 does not make it worse; 18.1.0 goes out when the pin PR chore(console): move the objectui pin 1d178e06ac5e → 6fb31dcc9954, past objectui#13370, so the console builds against the spec again objectstack#25455 lands.priority:p1,pm:queue,domain:ui— first in the objectuidomain:uiseat's queue. The fix ships with the next console pin bump after 18.1.0.Session:
session_01AkmasAfXt3cu83xGzkuKhe(objectstack triage, relaying the maintainer's ruling)Generated by Claude Code