Repository navigation
Description
Environment
- opencode: v1.14.48
- OS: macOS 15.4.1 (Apple Silicon arm64)
- Install method: npm (
npm i -g opencode-ai)
Summary
This is a deeper root cause analysis of #27071. The blank TUI is not caused by ENAMETOOLONG errors or by scanning ~/Library content. The true cause is FSEventStreamSetExclusionPaths taking 21-35 seconds to initialize when the exclusion paths (built from os.homedir()) point to directories with millions of files.
Root Cause
Startup Flow
os.homedir() → build exclusion paths → FSEventStreamSetExclusionPaths() → wait → file.watcher.updated → TUI renders
What happens
- On startup, opencode builds a list of "protected paths" from
os.homedir():var home = os.homedir(); var dirs = ["Music","Pictures","Movies","Downloads","Desktop","Documents","Public","Applications","Library"]; // + sub-paths under Library: Application Support, Caches, etc.
- These paths are passed to
FSEventStreamSetExclusionPaths()to exclude them from FSEvents monitoring FSEventStreamSetExclusionPathsinteracts with the volume's FSEvents database to register exclusions- When exclusion paths point to directories with millions of files (e.g., a 150GB
~/Library), this registration takes 21-35 seconds - The TUI blocks on
file.watcher.updatedevent which only fires after FSEvents initialization completes - User sees a blank page for 21-35 seconds
Key Evidence
| Test | Result |
|---|---|
HOME=/minimal/dir (exclusion paths → empty dirs) |
TUI renders in <1s |
HOME=/Users/town (exclusion paths → large dirs) |
TUI blank for 21-35s |
HOME=/Users/town + /tmp clean project dir |
Still 21-35s delay (not project dir issue) |
| Ripgrep scan of project dir | 14ms for 12 files (not ripgrep bottleneck) |
| Network blocked (sandbox-exec) | Still 21-35s (not network issue) |
| Fresh data directory | Still 21-35s (not data corruption) |
Log timing: file.watcher.updated subscribing → publishing |
Consistent 21-35s gap |
The delay is purely caused by os.homedir() returning a HOME with large directories, causing FSEventStreamSetExclusionPaths to be slow.
Why the HOME isolation workaround works
Setting HOME=/path/to/minimal/home makes os.homedir() return that path. The exclusion paths then point to tiny/empty directories, and FSEvents initializes instantly.
Proposed Fixes (in source code)
Fix 1: Don't block TUI on file watcher initialization (Recommended)
Render the TUI immediately instead of waiting for file.watcher.updated:
Current: startup → FSEvents init (blocks 21-35s) → render TUI
Proposed: startup → render TUI (immediate) → FSEvents init (background)
The file tree can show a loading state and refresh when the watcher is ready. This is a small change with maximum impact.
Fix 2: Only exclude paths under the watched directory
If the file watcher only monitors the project directory (not HOME), there's no need to exclude HOME subdirectories. The exclusion paths should only cover subdirectories of the actual watched path, not all of os.homedir().
Fix 3: Add timeout with fallback
If FSEventStreamSetExclusionPaths takes longer than N seconds, skip waiting and use a fallback (polling or no exclusion).
Workaround (for users)
Create a launcher script that sets HOME to an isolated directory:
#!/bin/bash
export HOME="/path/to/minimal/home"
mkdir -p "$HOME/.config/opencode"
# Copy or link your opencode.json config
exec opencode "$@"Related
- TUI shows blank page when ~/Library contains large number of files #27071 (original report, attributed to Library scanning - this issue provides the precise root cause)
- TUI renders blank when project has node_modules without .gitignore exclusion #27072 (node_modules without .gitignore - related ENAMETOOLONG issue but different root cause)
Activity
litown commentedon May 14, 2026
Corrected Root Cause Analysis
After further source code investigation, the root cause identified above is incorrect. The blank page is not caused by
FSEventStreamSetExclusionPaths.Actual Root Cause
The TUI blank page is caused by
SyncProvider.bootstrap()inpackages/opencode/src/cli/cmd/tui/context/sync.tsxblocking ALL rendering on ALL initial HTTP requests, includingapp.agents.Startup flow (actual):
The
blockingRequestsarray includes:config.providers✅ fastprovider.list✅ fastapp.agents❌ can hang foreverconfig.get✅ fastproject.sync✅ fastWhy
app.agentshangs: The server-side handler triggersAgent.Serviceinitialization via lazyInstanceState/ScopedCache. If the project directory contains deeply nestednode_modules/.pnpmpaths, the initialization encountersENAMETOOLONGerrors that are not properly propagated back to the HTTP handler, causing the request to hang indefinitely.Why TUI is blank: The
readygetter inSyncProvider:Combined with
createSimpleContext's<Show when={init.ready}>, the entire component tree is blocked until all blocking requests complete.Fix
PR #27448 moves
agentsPromisefromblockingRequeststo the non-blocking section. The TUI renders immediately after essential data loads, while agents load in the background.Why the HOME isolation workaround "worked"
The HOME isolation workaround didn't fix
FSEventStreamSetExclusionPaths— it changed the project context so thatAgent.Serviceinitialization no longer hit deeply nested paths. The real fix is making agents non-blocking.Correction on evidence
HOME=/minimal/dirworks/tmpclean project still hangsfile.watchergapapp.agentsHTTP requestI apologize for the initial misdiagnosis. The code-level investigation with the forked source confirmed the actual blocking point.
github-actions commentedon Jul 14, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.