Skip to content

Conversation

@JohnC0de
Copy link
Contributor

@JohnC0de JohnC0de commented Jan 6, 2026

Summary

Fixes a segmentation fault crash on Windows caused by Bun's ShellInterpreter garbage collection bug when using ctx.$ (Bun shell template literals) for notifications.

Problem

Users on Windows + Bun v1.3.x experience crashes after running oh-my-opencode for extended periods:

Segmentation fault at address 0x337081E00E0
- interpreter.zig:1239: deinitFromFinalizer
- ZigGeneratedClasses.zig:19925: ShellInterpreterClass__finalize
- MarkedSpace.cpp:256: JSC::MarkedSpace::sweepPreciseAllocations

Root Cause

This is a confirmed Bun runtime bug (oven-sh/bun#23177, oven-sh/bun#24368):

  • The ShellInterpreter's finalizer doesn't properly clear WriteBarrier fields before destruction
  • JavaScriptCore's garbage collector accesses freed memory during heap sweep
  • Happens when shell operations are used repeatedly (e.g., notification sounds/toasts)

Trigger in oh-my-opencode

The session-notification hook uses ctx.$ to run PowerShell commands on Windows:

// Before (triggers Bun GC bug)
await ctx.$`${powershellPath} -Command ${toastScript}`.catch(() => {})

Every notification creates a new ShellInterpreter object. Over time, GC tries to clean these up → crash.

Solution

Replace all ctx.$ (Bun shell) calls with node:child_process.spawn:

// After (uses stable Node.js API)
await execCommand(powershellPath, ["-Command", toastScript]).catch(() => {})

This bypasses Bun's shell interpreter entirely, using Node.js's stable child_process module.

Changes

File Change
src/hooks/session-notification.ts Replace ctx.$ with execCommand() using spawn from node:child_process
src/hooks/session-notification.test.ts Update mocks to spy on spawn instead of ctx.$

Testing

  • ✅ Typecheck passes
  • ✅ All 11 session-notification tests pass
  • ✅ Full test suite: 615 pass, 6 fail (pre-existing unrelated failures)

Upstream Status

Issue Status Notes
oven-sh/bun#23177 Closed (incomplete) PR #23624 merged but didn't fully fix
oven-sh/bun#24368 Open Same crash still happening in v1.3.1+
oven-sh/bun#24093 Open PR Real fix using jsc.JSRef pattern

Workaround Alternative

Users can also disable the hook entirely:

{ "disabled_hooks": ["session-notification"] }

Related


Summary by cubic

Replaced Bun shell template literals (ctx.$) with node:child_process.spawn in session-notification to prevent a Windows crash from Bun’s ShellInterpreter GC bug. Notifications and sounds now run reliably on Windows and consistently across platforms.

  • Bug Fixes
    • Prevents segmentation fault on Windows with repeated notifications (Bun v1.3.x).
    • Added execCommand wrapper and used it for osascript, notify-send, PowerShell, afplay/paplay/aplay.
    • Updated tests to mock child_process.spawn and track notification commands.

Written for commit 970a679. Summary will update on new commits.

…GC crash

Replace Bun shell template literals (ctx.$) with node:child_process.spawn
to work around Bun's ShellInterpreter garbage collection bug on Windows.

This bug causes segmentation faults in deinitFromFinalizer during heap
sweeping when shell operations are used repeatedly over time.

Bug references:
- oven-sh/bun#23177 (closed incomplete)
- oven-sh/bun#24368 (still open)
- Pending fix: oven-sh/bun#24093

The fix applies to all platforms for consistency and safety.
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

All contributors have signed the CLA. Thank you! ✅
Posted by the CLA Assistant Lite bot.

@greptile-apps
Copy link

greptile-apps bot commented Jan 6, 2026

Greptile Summary

Replaced Bun's shell template literals (ctx.$) with Node.js child_process.spawn in the session-notification hook to avoid a confirmed Bun runtime GC crash on Windows.

Changes:

  • Added execCommand() helper using node:child_process.spawn with proper error handling
  • Replaced all 7 ctx.$ calls (notifications + sound playback) with execCommand()
  • Updated test mocks to spy on spawn instead of the shell API
  • Maintains identical functionality while avoiding ShellInterpreter GC bug

Verification:

  • All 11 session-notification tests pass
  • No other files in the codebase use ctx.$
  • Workaround is valid until upstream Bun fix (oven-sh/bun#24093) is merged

Confidence Score: 5/5

  • This PR is safe to merge with no risk - it's a well-justified workaround for a confirmed upstream bug
  • The change addresses a confirmed Bun runtime bug with proper documentation and test coverage. The implementation correctly replaces shell operations with stable Node.js APIs while preserving functionality. All tests pass, the approach is sound, and the code quality is high.
  • No files require special attention

Important Files Changed

Filename Overview
src/hooks/session-notification.ts Replaced Bun shell (ctx.$) with node:child_process.spawn to avoid GC crash on Windows
src/hooks/session-notification.test.ts Updated test mocks to spy on spawn instead of ctx.$ template literal

Sequence Diagram

sequenceDiagram
    participant Session as OpenCode Session
    participant Hook as session-notification Hook
    participant Exec as execCommand()
    participant Spawn as node:child_process
    participant OS as OS (PowerShell/osascript/notify-send)

    Note over Session,OS: Before: Using Bun Shell (ctx.$)
    Session->>Hook: session.idle event
    Hook->>Hook: Wait idleConfirmationDelay
    Hook->>Hook: Check incomplete todos
    Hook->>OS: ctx.$`powershell -Command ...`
    Note over OS: ❌ Bun ShellInterpreter created
    OS-->>Hook: Notification sent
    Note over OS: ⚠️ GC attempts cleanup
    Note over OS: 💥 Segfault in finalizer

    Note over Session,OS: After: Using Node.js spawn
    Session->>Hook: session.idle event
    Hook->>Hook: Wait idleConfirmationDelay
    Hook->>Hook: Check incomplete todos
    Hook->>Exec: execCommand(powershell, ["-Command", script])
    Exec->>Spawn: spawn(cmd, args, {stdio: "ignore"})
    Spawn->>OS: Execute command
    OS-->>Spawn: Command completes
    Spawn-->>Exec: "close" event
    Exec-->>Hook: Promise resolves
    Note over Spawn: ✅ No ShellInterpreter
    Note over Spawn: ✅ Safe GC
Loading

@greptile-apps
Copy link

greptile-apps bot commented Jan 6, 2026

Greptile's behavior is changing!

From now on, if a review finishes with no comments, we will not post an additional "statistics" comment to confirm that our review found nothing to comment on. However, you can confirm that we reviewed your changes in the status check section.

This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR".

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@JohnC0de
Copy link
Contributor Author

JohnC0de commented Jan 6, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Jan 6, 2026
@code-yeongyu code-yeongyu merged commit 4a38e70 into code-yeongyu:dev Jan 6, 2026
4 of 5 checks passed
JohnC0de added a commit to JohnC0de/oh-my-opencode that referenced this pull request Jan 7, 2026
…ging

Adds a lightweight, off-by-default tracing system to help diagnose Windows crashes.

Features:
- Ring buffer-based tracer with JSONL output
- Traces all spawn operations (session-notification, LSP, subagents)
- Crash handlers (uncaughtException, unhandledRejection, signals)
- debug-bundle CLI command for diagnostic collection
- Automatic sensitive data redaction
- Enable via OMO_DEBUG=1 environment variable

This helps identify the root cause of crashes that persist after PR code-yeongyu#543.

Refs: oven-sh/bun#25798, anomalyco/opencode#3267
JohnC0de added a commit to JohnC0de/oh-my-opencode that referenced this pull request Jan 7, 2026
…iagnosed)

This reverts commit 4a38e70 (PR code-yeongyu#543) and 2064568 (follow-up fix).

## Why This Revert

The original diagnosis was incorrect. PR code-yeongyu#543 assumed Bun's
ShellInterpreter GC bug was causing Windows crashes, but further
investigation revealed the actual root cause:

**The crash occurs when oh-my-opencode's session-notification runs
alongside external notification plugins (e.g., @mohak34/opencode-notifier).**

Evidence:
- User removed opencode-notifier plugin → crashes stopped
- Release version (with original ctx.$ code) works fine when used alone
- No widespread crash reports from users without external notifiers
- Both plugins listen to session.idle and send concurrent notifications

The real issue is a conflict between two notification systems:
1. oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
2. opencode-notifier: node-notifier → SnoreToast.exe

A proper fix will detect and handle this conflict gracefully.

Refs: code-yeongyu#543, oven-sh/bun#23177, oven-sh/bun#24368
See: docs/CRASH_INVESTIGATION_TIMELINE.md (in follow-up commit)
JohnC0de added a commit to JohnC0de/oh-my-opencode that referenced this pull request Jan 7, 2026
…tification plugins

When oh-my-opencode's session-notification runs alongside external
notification plugins like opencode-notifier, both listen to session.idle
and send concurrent notifications. This can cause crashes on Windows
due to resource contention between different notification mechanisms:
- oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
- opencode-notifier: node-notifier → SnoreToast.exe

This commit adds:
1. External plugin detection (checks opencode.json for known notifiers)
2. Auto-disable of session-notification when conflict detected
3. Console warning explaining the situation
4. Config option 'notification.force_enable' to override

Known notification plugins detected:
- opencode-notifier
- @mohak34/opencode-notifier
- mohak34/opencode-notifier

This is the actual fix for the Windows crash issue previously
misdiagnosed as a Bun.spawn GC bug (PR code-yeongyu#543).

Refs: code-yeongyu#543
JohnC0de added a commit to JohnC0de/oh-my-opencode that referenced this pull request Jan 7, 2026
Documents the investigation journey from initial misdiagnosis (Bun GC bug)
to discovering the actual root cause (notification plugin conflict).

Key findings:
- PR code-yeongyu#543 was based on incorrect assumption
- The real issue is concurrent notification plugins
- oh-my-opencode + opencode-notifier = crash on Windows
- Either plugin alone works fine
code-yeongyu pushed a commit that referenced this pull request Jan 7, 2026
… plugin conflict detection (#575)

* revert: undo PR #543 changes (bun shell GC crash was misdiagnosed)

This reverts commit 4a38e70 (PR #543) and 2064568 (follow-up fix).

## Why This Revert

The original diagnosis was incorrect. PR #543 assumed Bun's
ShellInterpreter GC bug was causing Windows crashes, but further
investigation revealed the actual root cause:

**The crash occurs when oh-my-opencode's session-notification runs
alongside external notification plugins (e.g., @mohak34/opencode-notifier).**

Evidence:
- User removed opencode-notifier plugin → crashes stopped
- Release version (with original ctx.$ code) works fine when used alone
- No widespread crash reports from users without external notifiers
- Both plugins listen to session.idle and send concurrent notifications

The real issue is a conflict between two notification systems:
1. oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
2. opencode-notifier: node-notifier → SnoreToast.exe

A proper fix will detect and handle this conflict gracefully.

Refs: #543, oven-sh/bun#23177, oven-sh/bun#24368
See: docs/CRASH_INVESTIGATION_TIMELINE.md (in follow-up commit)

* fix(session-notification): detect and avoid conflict with external notification plugins

When oh-my-opencode's session-notification runs alongside external
notification plugins like opencode-notifier, both listen to session.idle
and send concurrent notifications. This can cause crashes on Windows
due to resource contention between different notification mechanisms:
- oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
- opencode-notifier: node-notifier → SnoreToast.exe

This commit adds:
1. External plugin detection (checks opencode.json for known notifiers)
2. Auto-disable of session-notification when conflict detected
3. Console warning explaining the situation
4. Config option 'notification.force_enable' to override

Known notification plugins detected:
- opencode-notifier
- @mohak34/opencode-notifier
- mohak34/opencode-notifier

This is the actual fix for the Windows crash issue previously
misdiagnosed as a Bun.spawn GC bug (PR #543).

Refs: #543

* docs: add crash investigation timeline explaining the real root cause

Documents the investigation journey from initial misdiagnosis (Bun GC bug)
to discovering the actual root cause (notification plugin conflict).

Key findings:
- PR #543 was based on incorrect assumption
- The real issue is concurrent notification plugins
- oh-my-opencode + opencode-notifier = crash on Windows
- Either plugin alone works fine

* fix: address review feedback - add PowerShell escaping and use existing JSONC parser

- Add back single-quote escaping for PowerShell soundPath to prevent command failures
- Replace custom stripJsonComments with existing parseJsoncSafe from jsonc-parser
- All 655 tests pass

---------

Co-authored-by: sisyphus-dev-ai <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants