Skip to content

opencode serve pegs CPU at ~1000% — fff content-indexes entire $HOME (search.ts enableHomeDirScanning/FsRootScanning) #32511

Closed

Description

@junbaor

Summary

When running opencode serve as a long-lived process whose working directory is $HOME, CPU climbs to ~1047% (≈10.5 cores) and stays high for 45+ minutes without converging. CPU sampling shows 100% of the load is the bundled fff file-search/index engine (@ff-labs/fff-bun) doing full-tree directory walks + per-file content indexing over the entire home directory — not just the project directory — because fffLayer enables enableHomeDirScanning + enableFsRootScanning. Setting OPENCODE_DISABLE_FFF=1 is a confirmed workaround.

Related issues: #31831 (same symptom — sustained high CPU on macOS ARM — but no root-cause), #31682 (closed; same fff+home root cause, but manifested as fff scan timed out crash rather than sustained CPU), #27082 / #27071 (earlier "large $HOME" analyses), #30086 (general high-CPU regression thread). This report adds the missing piece: a CPU sample stack + lsof that pin the load to fff content-indexing of $HOME, with the exact source location.

Environment

opencode 1.17.7 (Homebrew)
source commit 5d0f86606 (dev) — fix(mcp): stop idle OAuth callback server (#32245)
@ff-labs/fff-bun 0.9.4 (with patch @ff-labs/[email protected])
Bun 1.3.14
Node v26.3.0
OS macOS 26.5.1 (25F80), Darwin 25.5.0 / xnu-12377.121.6
Hardware MacBook Pro (Mac17,9), Apple M5 Pro, 15 cores, 48 GB RAM, arm64
Command opencode serve --hostname 0.0.0.0 --port 4096 (launchd, WorkingDirectory = /Users/<me>)

Observed behavior

  • CPU peaks at 1047%, oscillating 56%–1047% in a "scan a round, pause, scan again" pattern.
  • Physical memory footprint 3.3 GB and growing (RSS 1.4G → 1.8G during observation).
  • Runs 45+ min with no convergence; process stays R (running).

CPU time series:

13:46:18 1047.3%   <- peak
13:46:19 1045.0%
13:46:21  955.8%
13:46:25  372.5%

Root cause

1. CPU sample (30s) — load is entirely fff scan/index threads

Hot functions, all in fff's Rust scan/index threads (fff-scan, fff-bg-0..14):

428  fff_search::bigram_filter::BigramIndexBuilder::add_file_content   (reads each file's content -> bigram index)
 76  fff_search::file_picker::FileSync::walk_filesystem                (full-tree walk)
 28  fff_search::scan::ScanJob::run
 22  fff_search::types::FileItem::detect_binary_per_byte               (per-byte binary detection)
 14  fff_search::background_watcher::handle_debounced_events
  5  fff_search::scan::rescubscribe_watcher_post_scan                  (re-subscribe watcher after scan)

Representative stack:

fff_search::scan::ScanJob::run
  -> ignore::walk::WalkParallel::visit            (rayon parallel dir walk)
  -> openat / read / close                        (open each file)
      -> fff_search::bigram_filter::BigramIndexBuilder::add_file_content

2. lsof proves the scan scope is the whole user directory (not the project)

102 directory handles, including paths far outside any project:

/Users/<me>                 (home)
/Users                      (parent of home, toward fs root)
/Users/<me>/.local
/Users/<me>/Library
/Users/<me>/Downloads
/Users/<me>/Library/Android/sdk/.../drawable/*.xml   (even Android SDK assets)

3. Source location

packages/core/src/filesystem/search.ts (fffLayer):

Fff.create({
  basePath: location.directory,
  aiMode: true,
  enableFsRootScanning: true,    // <-- scans toward filesystem root
  enableHomeDirScanning: true,   // <-- scans the entire home directory
})

Note: the project's own benchmark packages/opencode/script/bench-search.ts creates the picker without these flags (Fff.create({ basePath: dir, aiMode: true })). So the fast numbers measured during development reflect project-scoped scanning, while the production fffLayer enables full home/root scanning. For a long-lived serve process — especially one whose WorkingDirectory is $HOME — this turns "index the project" into "full-content-index the entire home directory" (bigram index + per-byte binary detection over node_modules, caches, SDKs…), saturating all cores and producing multi-GB index memory.

4. Watcher duplication (amplifier — flagged as a hypothesis)

The sample shows 6 concurrent watcher thread groups:

fff-watcher-own            x6
notify-rs fsevents loop    x6
notify-rs debouncer loop   x6

Combined with rescubscribe_watcher_post_scan (re-subscribe after each scan) and a session that opened several projects over time, each scanned root appears to spawn its own fsevents watcher and re-subscribe after each scan. I have not confirmed a definitive leak, but the duplication is consistent with one.

Reproduction

  1. Run opencode serve with WorkingDirectory/cwd set to $HOME (or simply run from a home directory that contains many files — node_modules, SDKs, caches).
  2. Leave it running.
  3. Observe CPU climb to several hundred–thousand percent. sample <pid> shows fff_search::scan / BigramIndexBuilder::add_file_content; lsof <pid> shows directory handles all over $HOME.

Workaround (confirmed)

OPENCODE_DISABLE_FFF=1 opencode serve ...

Routes through ripgrepLayer (see search.ts defaultLayer and flag.ts OPENCODE_DISABLE_FFF) and skips full content indexing. Search still works (falls back to ripgrep), losing only fff's frecency/realtime features.

Suggested fix

For the serve path, set enableFsRootScanning / enableHomeDirScanning to false so fff only indexes location.directory. If home/root scanning backs a real feature (cross-project navigation, frecency history), bound it instead — exclusion rules, scan throttling, or lazy/on-demand triggering — and ensure watchers are deduplicated and torn down per project. A guard against location.directory === os.homedir() would also prevent the worst case.


I have full sample output (10s + 30s), lsof, vmmap, the CPU time series, and server logs captured locally and can attach any of them on request.

Activity

  1. github-actions commented on Jun 16, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your issue is distinct (specifically the fff enableHomeDirScanning/enableFsRootScanning root cause when CWD is $HOME), it may still be worth keeping open as a separate actionable report with the specific source location and fix suggestion. Please cross-reference these threads so maintainers can triage effectively.

  2. added a commit that references this issue on Jul 5, 2026
  3. vcode11 commented on Jul 15, 2026

    @vcode11

    +1 disabled fff with OPENCODE_DISABLE_FFF=1

  4. jjangga0214 commented on Aug 22, 2026

    @jjangga0214

    Additional reproduction/data point on the current release.

    Environment:

    • OpenCode 1.18.21 TUI
    • macOS 26.5.2 (25F84), arm64
    • WarpTerminal
    • OpenCode launched with cwd=$HOME
    • Plugins: opencode-gpt-imagegen and a local rtk.ts plugin

    Observed behavior:

    • After using several OpenCode sessions concurrently, all sessions were inactive but CPU and memory usage continued to surge.
    • The TUI processes were exited normally, not force-killed.
    • After restarting, resource use initially settled, so the stuck high-CPU state is transient and cleared by restart.
    • A fresh/current single TUI process still reached about 1,400,848 KB RSS after roughly 8 minutes of normal use. vmmap reported a 675.3 MB physical footprint (878.4 MB peak), with WebKit Malloc showing about 969.1 MB resident / 540.4 MB dirty.

    Potentially important home-scan amplifier:

    • $HOME/.local/share/opencode/opencode.db is 38,514,364,416 bytes (about 35.9 GiB), 9,402,942 SQLite pages with zero freelist pages.
    • The DB contains 1,398 sessions (536 root / 862 child), 48,784 messages, 262,105 parts, and 739,568 events.
    • Because OpenCode was launched at $HOME, FFF home scanning may be traversing/indexing this very large OpenCode database and the rest of the home directory. This matches the trigger and symptoms described in this issue.
    • One Serena MCP Python child was idle at 0% CPU and about 183 MB RSS; the high resource process was OpenCode itself.

    I do not have a preserved CPU profile from the transient idle spike yet, and have not yet compared the same workload with OPENCODE_DISABLE_FFF=1. I will capture a macOS sample and run that A/B test if it reproduces.

  5. neriousy commented on Oct 19, 2026

    @neriousy
    Member

    This is fixed in v2: home directories and non-VCS locations use ripgrep instead of fff, so starting OpenCode in your home directory no longer triggers fff content-indexing of that tree. The fallback scan is capped at 100,000 entries and excludes protected home-directory folders. To upgrade, run npm i -g @opencode/cli@latest. If you still see this on v2, please open a new issue with the details.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions