Skip to content

fix(tui): guard against null/undefined MCP status in reactive computations#22206

Open
KeWang0622 wants to merge 1 commit intoanomalyco:devfrom
KeWang0622:fix/mcp-null-guard-tui-crash
Open

fix(tui): guard against null/undefined MCP status in reactive computations#22206
KeWang0622 wants to merge 1 commit intoanomalyco:devfrom
KeWang0622:fix/mcp-null-guard-tui-crash

Conversation

@KeWang0622
Copy link
Copy Markdown

Issue for this PR

Closes #22102
Closes #21931
Closes #21645
Closes #21449
Closes #21290
Closes #20806

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

During TUI bootstrap, sync.tsx fetches MCP status from the server and updates the store via:

sdk.client.mcp.status({ workspace }).then((x) => setStore("mcp", reconcile(x.data!)))

When x.data is null or undefined (e.g., the server hasn't finished initializing, a network hiccup, or an SDK edge case), reconcile(undefined) sets store.mcp to undefined. Every component that then calls Object.entries(sync.data.mcp), Object.keys(...), or Object.values(...) throws:

TypeError: Object.entries requires that input parameter not be null or undefined

This crash has been reported at least 6 times (#22102, #21931, #21645, #21449, #21290, #20806), primarily on Windows but reproducible on any platform with the right timing.

Root fix (sync.tsx): replace x.data! with x.data ?? {} so the store is never set to undefined.

Defense-in-depth (5 consumption sites): add ?? {} or ?. at every call site that touches sync.data.mcp, so even if the store value is transiently undefined during a reactive recomputation, no component crashes. This matches the pattern already used in dialog-mcp.tsx (line 35: mcpData ?? {}).

Files changed

File Change
context/sync.tsx x.data!x.data ?? {} (root cause)
component/dialog-status.tsx sync.data.mcpsync.data.mcp ?? {} (3 sites)
routes/session/footer.tsx sync.data.mcpsync.data.mcp ?? {} (2 sites)
plugin/api.tsx sync.data.mcpsync.data.mcp ?? {} (1 site)
context/local.tsx sync.data.mcp[name]sync.data.mcp?.[name] (2 sites)

How did you verify your code works?

  • bun test — all 1893 tests pass (0 fail)
  • bun typecheck — all 13 packages pass (the husky pre-push hook ran this automatically)
  • Manually inspected every sync.data.mcp access in the TUI codebase to confirm all are now guarded

Screenshots / recordings

N/A — not a visual change; this prevents a crash.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…tions

When the MCP status API returns null data, `reconcile(x.data!)` sets
`sync.data.mcp` to undefined, crashing every component that calls
Object.entries/keys/values on it.

Root fix: replace `x.data!` with `x.data ?? {}` in sync bootstrap.
Defense-in-depth: add `?? {}` / `?.` at all six consumption sites so a
stale store value can never reach Object.entries.

Closes anomalyco#22102
Closes anomalyco#21931
Closes anomalyco#21645
Closes anomalyco#21449
Closes anomalyco#21290
Closes anomalyco#20806
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

I found potentially related PRs:

PR #20444: fix(tui): add null guards for lsp and mcp state data

#20444

  • This appears to address a similar issue with null guards for MCP state data in the TUI

PR #21246: fix(tui): guard against undefined agents and mcp state during bootstrap

#21246

Both PRs are addressing similar null/undefined guard issues in MCP state handling during TUI initialization, though PR #22206 appears to be a more comprehensive fix across multiple consumption sites.

@KeWang0622
Copy link
Copy Markdown
Author

Thanks for flagging the related PRs!

How this PR differs from #20444 and #21246:

This PR and #21246 are complementary (MCP root cause + agent guards). #20444 is fully subsumed by this one.

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