Repository navigation
fix(messages): tolerate a cancelled reaction-name chunk load - #3450
Conversation
useReactionName and ReactionEmoji's revealName both ran
`void import("./reaction-name").then(...)` with no rejection handler.
reaction-name.ts statically imports the emoji-mart native table, so the
first Unicode status or reaction name starts a large chunk load. When a
navigation lands while that load is in flight, WebKit cancels the request,
the import() rejects, and the page reports "Unhandled Promise Rejection:
TypeError: Importing a module script failed." In the user-status journey
the "Free-text Bob" click starts the load and the next page.goto cancels
it (the traces show "Load request cancelled" 4 ms after the goto), and the
spec's deliberate check for unexplained page errors failed the test on
WebKit in CI.
Handle the rejection at both sites. A failed load leaves `loaded`
undefined, so the next mount of the hook tries again; ReactionEmoji keeps
the raw content and the next hover or focus tries again. No spec or
fixture change: the page-error check stays as it is.
Unit coverage: use-reaction-name.test.tsx and a MessageReactions case
mock ./reaction-name to reject, assert the name stays unresolved with no
unhandledRejection reaching the process, then unmock and show the next
mount/reveal resolves (:house: / :+1:). On the unfixed code both fail
with the unhandled rejection; with the fix all 27 tests in the two files
pass.
Verified with `bin/pnpm test:browser:ci --project webkit --no-deps
--workers=1 --repeat-each=8 --reporter=list
tests/browser/user-status.spec.mjs`: 8/8 passed locally (11.7–12.7 s
each, 1.7 m). The cancelled chunk did not reproduce on an idle host; the
triage reproduced it deterministically by holding the native.json request
across the navigation (3/3 fail unfixed, 3/3 pass fixed), so the local
repeat guards against regression rather than reproducing CI timing. Biome
and tsc --noEmit are clean.
The separate "Duration: Today" vs "8 hours" tie in statusDuration at
exactly 16:00 UTC is not addressed here.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: mtoohey <[email protected]>
Signed-off-by: Matt Toohey <[email protected]>
The rejection handlers added for a cancelled reaction-name chunk load were empty, which made a chunk that keeps failing, such as a 404 after a deploy with a stale shell, silent. useReactionName and ReactionEmoji's revealName now console.warn with the error, as the rest of src does. No browser check changes: the fixture records only error-level console messages and page-errors.mjs watches pageerror. The comments now say that only a new document fetches the chunk again, since some engines keep a failed module fetch for the document's life and reject at once. The two unit tests spy on console.warn and assert the warning, waiting for it with waitFor: the mocked import rejects after a nondeterministic number of ticks and a single flush was not always enough (one of six local runs saw no call at assertion time before the wait was added). Verified as part of the combined review follow-up, before it was split by path: - vitest run use-reaction-name.test.tsx MessageReactions.test.tsx: 27 passed in 8 of 8 runs, with no stderr output. - bin/pnpm test:browser:ci --project webkit --no-deps --workers=1 tests/browser/user-status.spec.mjs tests/browser/composer-attachments.spec.mjs: 11 passed (1.4 m), including user-status's unexplained page-error check. - biome check --error-on-warnings on the touched files and tsc --noEmit are clean. Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: mtoohey <[email protected]> Signed-off-by: Matt Toohey <[email protected]>
kazemek21
left a comment
There was a problem hiding this comment.
Kyle reviewed the PR and the Nexus review-tracker notes. Approved with no comments.
Posted by Codex (gpt-6.1-sol) on behalf of kylez.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord automated source review via Wes’s account.
No actionable findings at cf0bd7fe53810a1835cc77600a3e7d5bb6f14583 against base/merge-base 7f03764beb8eb0ddcb4ff9ec0b8bda3e07699716.
Both dynamic imports handle rejection while preserving the existing lazy-load triggers, successful lookup behavior and raw/unresolved fallback. The hook retains its unmount guard. The new tests await the failure handler before checking fallback, then exercise another mount or focus reveal.
Source-only review: no tests, builds or app execution performed. The mocked retry cases do not establish browser refetch after a cached module failure, or reproduce WebKit navigation cancellation. Hosted checks in the inspected current-head snapshot passed, with Windows native validation skipped; desktop, screen-reader and human acceptance remain unverified. The PR description distinguishes current-head checks from earlier combined-branch evidence.
…y-unreads * origin/main: (24 commits) [1/2] feat(agents): add a recommended agent setup (core) (#3344) test(browser): survive the live-stream restart a page-clock jump causes (#3442) chore(renovate): hold rusqlite below 0.41 for mesh-llm (#3438) test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (#3445) test(browser): play the draft clip at rate 0 instead of looping it before the Play click (#3446) fix(status): reopen a status whose preset deadlines coincide as Today (#3449) fix(messages): tolerate a cancelled reaction-name chunk load (#3450) fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (#3451) test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (#3444) Expose native channel headings to Solo sources (#3523) test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (#3447) fix(solo): keep every contributed destination action (#3516) fix(identity): report an issuer that keeps rate-limiting an agent as a limit (#2365) fix(identity): let a stuck at-launch sign-in copy its link (#2453) fix(profiles): open avatar previews on hover in compact density (#3510) feat(solo): smooth chat recency reordering (#3502) Reject self-mentions that cannot wake another session (#3513) feat(status): offer don't clear for every status (#3515) Let plugin panels name their tab with panels.useTabTitle (#3251) fix(agents): report a conversation wake that fails before its turn starts (#2192) ... # Conflicts: # tests/browser/message-management.spec.mjs
* origin/main: (27 commits) [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (#3345) fix(solo): preserve and resume unsent chat drafts (#3525) fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (#3078) fix(agents): say why Import an agent can't import here, and link Manage plugins (#3073) [1/2] feat(agents): add a recommended agent setup (core) (#3344) test(browser): survive the live-stream restart a page-clock jump causes (#3442) chore(renovate): hold rusqlite below 0.41 for mesh-llm (#3438) test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (#3445) test(browser): play the draft clip at rate 0 instead of looping it before the Play click (#3446) fix(status): reopen a status whose preset deadlines coincide as Today (#3449) fix(messages): tolerate a cancelled reaction-name chunk load (#3450) fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (#3451) test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (#3444) Expose native channel headings to Solo sources (#3523) test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (#3447) fix(solo): keep every contributed destination action (#3516) fix(identity): report an issuer that keeps rate-limiting an agent as a limit (#2365) fix(identity): let a stuck at-launch sign-in copy its link (#2453) fix(profiles): open avatar previews on hover in compact density (#3510) feat(solo): smooth chat recency reordering (#3502) ... # Conflicts: # src-tauri/src/federated_identity/agent/tests.rs # src-tauri/src/federated_identity/mod.rs # src-tauri/src/federated_identity/protocol.rs
* origin/main: (133 commits) Expose ordinary chats without workspace headings (block#3530) [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (block#3345) fix(solo): preserve and resume unsent chat drafts (block#3525) fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (block#3078) fix(agents): say why Import an agent can't import here, and link Manage plugins (block#3073) [1/2] feat(agents): add a recommended agent setup (core) (block#3344) test(browser): survive the live-stream restart a page-clock jump causes (block#3442) chore(renovate): hold rusqlite below 0.41 for mesh-llm (block#3438) test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (block#3445) test(browser): play the draft clip at rate 0 instead of looping it before the Play click (block#3446) fix(status): reopen a status whose preset deadlines coincide as Today (block#3449) fix(messages): tolerate a cancelled reaction-name chunk load (block#3450) fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (block#3451) test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (block#3444) Expose native channel headings to Solo sources (block#3523) test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (block#3447) fix(solo): keep every contributed destination action (block#3516) fix(identity): report an issuer that keeps rate-limiting an agent as a limit (block#2365) fix(identity): let a stuck at-launch sign-in copy its link (block#2453) fix(profiles): open avatar previews on hover in compact density (block#3510) ... Signed-off-by: Joah Gerstenberg <[email protected]> # Conflicts: # src/bundled/codex/prompt.test.ts # src/bundled/goose/runtime.ts
Why
user-status.spec.mjs, "statuses edit, synchronize, clear, reject stale traffic and retain failed drafts", fails intermittently on WebKit with an unexplained page error:Unhandled Promise Rejection: TypeError: Importing a module script failed.The spec's check for unexplained page errors is deliberate and correct. The bug is in the product.
Root cause
useReactionNameandReactionEmoji'srevealNameboth ranvoid import("./reaction-name").then(...)with no rejection handler.reaction-name.tsstatically imports the emoji-mart native table, so the first Unicode status or reaction name starts a large chunk load. When a navigation lands while that load is in flight, WebKit cancels the request, theimport()rejects, and the page reports an unhandled rejection.In the journey, the click that opens a free-text status starts the load and the next
page.gotocancels it. The traces show the chunk request failing with "Load request cancelled" and the page error 4 ms after the navigation. The unguarded import arrived with #2384 and moved intouseReactionNamein #2774.Fix
Handle the rejection at both sites.
ReactionEmojikeeps the raw content and the next hover or focus tries again.console.warnwith the error, so a chunk that keeps failing, such as a 404 after a deploy with a stale shell, stays visible. The comments note that only a new document fetches the chunk again, because some engines keep a failed module fetch for the document's life.The warning does not affect browser checks: the fixture records only error-level console messages, and the page-error helper watches
pageerror. There is no spec or fixture change, and the page-error check stays as it is.The second commit is the review follow-up that replaced the first commit's empty handlers with the warning and made the two unit tests wait for it.
Tests
use-reaction-name.test.tsx(new) and aMessageReactions.test.tsxcase mock./reaction-nameto reject. They assert the name stays unresolved, that the warning is logged, and that no unhandled rejection reaches the process. They then unmock and show the next mount or reveal resolves.Evidence
bin/pnpm exec vitest run src/features/messages/use-reaction-name.test.tsx src/features/messages/MessageReactions.test.tsxuser-status.spec.mjs,--workers=1 --repeat-each=8tsc --noEmitThe count is 29 here and 27 in the commit messages because #3397 has since added tests to
MessageReactions.test.tsx.Not reproduced, not run
buzz-review-completedis not set.Left alone
MessageReactions.tsx, and fix(reactions): let people dismiss a failed reaction delivery #2794, fix: send one deletion per target so linked-copy deletes succeed #2154 and feat(reactions): list everyone who reacted from a grouped "and N others" #2120 edit its test file. A trial merge of each against this change is clean.🤖 Generated with Claude Code