Skip to content

fix(desktop): treat unavailable collaboration authority as an empty inbox - #4548

Closed
bytelazy wants to merge 2 commits into
apache:mainfrom
bytelazy:fix/desktop-collaboration-inbox-unavailable
Closed

fix(desktop): treat unavailable collaboration authority as an empty inbox#4548
bytelazy wants to merge 2 commits into
apache:mainfrom
bytelazy:fix/desktop-collaboration-inbox-unavailable

Conversation

@bytelazy

@bytelazy bytelazy commented Sep 2, 2026

Copy link
Copy Markdown

Summary

With the default Local Runtime Host (which has no collaboration authority),
Maka Desktop polls collaboration.turn-request.query on a 2s inbox interval.
Each query rejects with operation_unavailable, and because that is the only
owner Host, collectAvailablePendingTurnRequests threw an AggregateError.
The renderer caught it and scheduled another poll, so Electron logged the same
rejected IPC handler call roughly every two seconds after startup — an
unbounded stream of identical errors that buries real diagnostics.

An unavailable collaboration capability is a valid Host composition, not a
failure. This change makes collectAvailablePendingTurnRequests resolve to an
empty inbox when no Host answered, instead of throwing. The poller then keeps a
quiet, empty inbox and repopulates it as soon as a capable Host appears.

Behavioral notes:

  • Mixed-capability setups are unchanged: a Host that still answers keeps
    contributing its requests (the partial-failure path already dropped rejected
    queries and is untouched).
  • The empty result is transient by construction — the existing 2s poll
    repopulates the inbox on the next tick once a collaboration-capable Host is
    present, so a momentary outage resolves to a briefly empty inbox rather than
    an error log.

Fixes #4522

Verification

  • Ran the inbox unit tests with node --experimental-strip-types (Node
    v24.20.0): both pass, including the updated returns an empty inbox when every Owner Host rejects case.
  • Confirmed the updated test fails against the old throwing implementation (it
    surfaces the AggregateError), so the test pins the new behavior.

Not run: the full desktop lint/typecheck/test suites and a live Desktop launch
(a full npm ci would not complete from this network). The change is confined
to collectAvailablePendingTurnRequests and its test; the throw had no other
callers depending on it (the only production caller is getPendingTurnRequests
in preload.ts, and the poller already catches and ignores rejections).

Root cause

The inbox fan-out treated "every Host rejected" as a hard failure. But the
default Local Host legitimately has no collaboration authority, so on a fresh
Desktop startup every query rejects with operation_unavailable, the helper
threw, and the 2s retry loop turned one static condition into a repeating IPC
error.

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — traced the failure across preload/main/host,
implemented the change and test update, and authored the commit. The commit
carries a Generated-by: Claude (Claude Code) trailer.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

…dows

The desktop stories entry was built with resolve(REPO_ROOT, ...), which
produces a backslash absolute path on Windows. Glob matchers treat
backslashes as escape characters, so the pattern matched nothing and the
desktop stories were silently dropped from the index (53 entries instead
of 251). The neighboring packages/ui entry is a forward-slash,
config-relative glob, which is why only the UI stories kept working.

Use the same config-relative form for the desktop entry so the glob
matches on all platforms.

Fixes apache#4516

Generated-by: Claude (Claude Code)
…nbox

The turn-request inbox polls every owner Runtime Host on a 2s interval.
A Host with no collaboration authority (e.g. the default Local Host)
rejects each query with operation_unavailable, and when every Host
rejected, collectAvailablePendingTurnRequests threw an AggregateError.
The renderer caught it and retried, so Electron logged the same rejected
IPC handler call every two seconds after startup — an unbounded stream of
identical errors that buries real diagnostics.

An unavailable collaboration capability is a valid Host composition, not
a failure, so resolve the inbox as empty when no Host answered instead of
throwing. The poller then keeps a quiet, empty inbox and repopulates it
as soon as a capable Host appears. Mixed-capability setups are unchanged:
a Host that still answers keeps contributing its requests.

Fixes apache#4522

Generated-by: Claude (Claude Code)
@github-actions github-actions Bot added the effort/S Under 100 readable lines label Sep 2, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem in #4522 is real and the reproduction is complete, but this change sits after the point where the symptom is produced, so I do not think it fixes it.

The line the issue quotes, Error occurred in handler for 'session-collaboration:turn-request:query', is printed by Electron's main process when an ipcMain.handle callback rejects, before the rejection reaches the renderer side. The query is handled in runtime-host-collaboration-ipc-main.ts through handleReconnectableRead, and operation_unavailable is not a reconnectable failure, so the handler rejects, Electron logs, and only then does collectAvailablePendingTurnRequests in preload see the settled promise. Swallowing the aggregate there changes what the renderer receives; it does not change what the terminal prints every two seconds, and it does not reduce the polling or the Host traffic the issue also asks about. I read this from the local Electron main bundle rather than running Desktop, so if you have observed the log stop, please say how.

The seam that would work is the main-process handler: readWithFallback in ipc-reconnect-policy.ts already exists for reads that should degrade to a value rather than fail, and runtime-host-search-ipc-main.ts uses it. A variant that maps only operation_unavailable to { requests: [] } at the handler keeps every other rejection as a rejection, and the test belongs on the handler map fixture in runtime-host-collaboration-ipc-main.test.ts.

Doing it in preload also removes a contract that a test was pinning. Before, all-reject threw and the renderer's catch kept the previous projection while a Host reconnects (the comment in use-turn-request-inbox.ts says exactly that). Now an all-reject applies an empty projection, so when the reconnect window is exhausted or the target changes, pending turn requests vanish from the inbox. The renamed test returns an empty inbox when every Owner Host rejects locks in the regression. Distinguishing "no authority" from "read failed" at the handler makes this disappear on its own.

Two things on the PR itself. Commit f1d46d59 is the Windows Storybook glob for #4516; it is the head of your #4541 and has no merge constraint with this fix, so please rebase this branch onto main with only 66d0398e. And "stop polling" in the issue title needs the Host to advertise its authorities, which today it does not (HostAccepted carries only epoch, composition and state), so that part is follow-up work; the body should say so rather than Fixes #4522 closing an issue that still reproduces.

Evidence boundary: static read of 66d0398e against main 61224f51; desktop test:dist, typecheck, architecture check, lint and format run locally and green; Electron log path traced from the main-process bundle, Desktop not launched.

AI-assisted review: drafted with Maka; I verified the handler path, the replaced test and the shared commit with #4541 myself.

简体中文

#4522 的问题真实,但这个改动落在症状产生点之后:那条日志是 Electron 主进程在 ipcMain.handle 回调 reject 时打的,早于 preload 收到结果,所以 preload 吞掉聚合错误既不减少日志,也不减少轮询和 Host 流量。正确的接缝是主进程 handler,复用 ipc-reconnect-policy.ts 里已有的 readWithFallback,只把 operation_unavailable 映射成空结果。现在的改法还删掉了「Host 全拒时保留上次投影」的契约,重连窗口耗尽后 inbox 里的 pending 请求会消失,改名后的测试把这个回归钉死了。另外 f1d46d59#4541 的 head,与本修复无关,请 rebase 只留 66d0398e;「停止轮询」需要 Host 广播能力,属后续工作,正文别写 Fixes。

'Every Runtime Host collaboration inbox request failed',
);
}
// A Host with no collaboration authority rejects every inbox query with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the time this runs, Electron has already printed Error occurred in handler for ... in the main process for each rejected invoke. The degrade has to happen in the ipcMain.handle callback in runtime-host-collaboration-ipc-main.ts, and only for operation_unavailable; every other rejection should keep throwing so the renderer keeps its last projection.

@bytelazy

bytelazy commented Sep 3, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed review and guidance @Astro-Han! I see that commit 72eb982 (#4557) has already landed the exact main-process handler fix with and added the test in .

Closing this PR as the issue is resolved on . Appreciate the pointers on the proper seam!

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

Labels

effort/S Under 100 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(desktop): stop polling unavailable collaboration authority

2 participants