fix(tui): guard against undefined agents and mcp state during bootstrap#21246
fix(tui): guard against undefined agents and mcp state during bootstrap#21246surajrav wants to merge 1 commit into
Conversation
The TUI renders components before async bootstrap completes, causing crashes when plugins (e.g. oh-my-openagent) slow down agent/mcp resolution. Guard all accesses to potentially empty sync.data fields to prevent TypeError on undefined property access.
|
The following comment was made by an LLM, it may be inaccurate: Potentially Related PR Found: PR #17015: Fix/issue 16982 agent current undefined
Note: The current PR is explicitly addressing issues #20588 and #20628, and the search results predominantly return PR #21246 itself, which indicates no other open PRs are currently tackling the same defensive null guards and bootstrap timing issues. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
Issue for this PR
#20588, #20628.
Closes #
Fixes #20588, #20628.
Type of change
What does this PR do?
Disclaimer: I made this fix with the help of opencode itself with claude opus 4.6, but I have spent time understanding what it is doing AND tested it to work in my local setup
All changes are defensive null guards. No behavioral change when data is present.
Problem
The TUI crashes with a fatal TypeError when plugins like oh-my-openagent are loaded:
TypeError: undefined is not an object (evaluating 'list2().length')
at (/$bunfs/root/src/index.js:496761:41)
at runComputation (/$bunfs/root/src/index.js:472905:24)
at updateComputation (/$bunfs/root/src/index.js:472888:17)
at readSignal (/$bunfs/root/src/index.js:472810:24)
at (/$bunfs/root/src/index.js:473750:49)
at runComputation (/$bunfs/root/src/index.js:472905:24)
at updateComputation (/$bunfs/root/src/index.js:472888:17)
at createMemo (/$bunfs/root/src/index.js:472393:22)
at Show (/$bunfs/root/src/index.js:473750:36)
Running from source reveals the crash originates in footer.tsx:30 and local.tsx:43.
Root Cause
SyncProvider initializes sync.data with empty defaults (agent: [], mcp: {}) and calls bootstrap() asynchronously via onMount. SolidJS continues rendering child components (LocalProvider, footer, sidebar) before bootstrap resolves.
Without plugins, the server responds fast enough that the timing window is negligible. When oh-my-openagent is loaded, its config handler performs model resolution, skill discovery, and provider caching, which delays the sdk.client.app.agents() response. This widens the window where sync.data fields are still in their initial empty state, and the TUI crashes accessing:
Files Changed and Why
context/local.tsxagents()[0].nameagents()[0]?.name ?? ""agents()is[]before bootstrap resolves, soagents()[0]isundefined. Accessing.nameonundefinedthrows the fatal TypeError.context/local.tsxsetAgentStore("current", value.name)if (value) setAgentStore("current", value.name)agents()[next]can beundefinedif the list is empty when user cycles agents via keybind before bootstrap completes.plugin/api.tsxObject.entries(sync.data.mcp)if (!sync.data.mcp) return []before theObject.entriescallsync.data.mcpcan beundefinedafterreconcile(undefined)from a server response wherex.dataisundefined.Object.entries(undefined)throws.feature-plugins/home/footer.tsxprops.api.state.mcp()props.api.state.mcp() ?? []list2().length).state.mcp()returnsundefinedbefore bootstrap, and line 23 calls.lengthon it.feature-plugins/sidebar/mcp.tsxprops.api.state.mcp()props.api.state.mcp() ?? [].length,.filter(),.some()on the result, all of which crash onundefined.component/dialog-agent.tsxlocal.agent.current().namelocal.agent.current()?.name ?? ""current()returnsagents().find(...) ?? agents()[0]— both returnundefinedwhen agents list is empty. Accessing.namecrashes.How did you verify your code works?
I ran opencode from my fork and didn't face the crash and got my prompt screen.
Screenshots / recordings
Checklist