Bug: Bun C++ Exception Crash with PartyWebSocket (partysocket library)
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 20/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 停滞
- 技術スタック
- typescript
- 領域
- networking
調査の方向性
backend/apps/support/src/lib/chat-api-client.ts から始め、issue で説明されている PartyWebSocket と native WebSocket のパスを比較します。Bun v1.3.5 上で [email protected] を使って長時間の再現を実行し、その後、parallel-GC crash が発生せず、報告されている 3–4 分の間隔を超えてアプリケーションが実行され続けることを確認します。関連する Bun issue が追加のコンテキストを提供する可能性があります。
索引モデルが issue の本文から書いたものです。
説明
Summary
Bun v1.3.5 crashes with a "pure virtual method called" C++ exception after running for 3-4 minutes when using PartyWebSocket from the partysocket library. The crash occurs during JavaScriptCore's parallel garbage collection when marking WebCore/DOM objects.
Environment
- Bun version: v1.3.5 (1e86cebd)
- OS: Linux x86_64 (baseline)
- Kernel: v6.18.0 | glibc v2.42
- Library:
[email protected](via catalog) - Crash time: Consistently after 3-4 minutes of runtime
Stack Trace
Bun v1.3.5 (1e86cebd) on linux x86_64_baseline [AutoCommand]
**panic**: A C++ exception occurred
- JSGlobalObject.zig:854: `Zig__GlobalObject__onCrash`
- compiler_builtins.6c1f39da0e88e811-cgu.247:0: `__cxxabiv1::__terminate`
- ClassInfo.h:124: `WTF::Ref<WTF::SharedTask<void (...)>, WTF::RawPtrTraits<WTF::SharedTask<void (...)> >, WTF::DefaultRefDerefTraits<WTF::SharedTask<void (...)> > > JSC::Subspace::forEachMarkedCellInParallel<JSC::SlotVisitor, void WebCore::DOMGCOutputConstraint::executeImplImpl<JSC::SlotVisitor>(...)::'lambda'(...)::operator(...) const::'lambda'(...)>(...)::'lambda'(...)::operator(...) const::'lambda'(...) const&)::Task::run`
- MarkingConstraint.cpp:96: `JSC::MarkingConstraintSolver::runExecutionThread`
- SlotVisitor.cpp:686: `JSC::SlotVisitor::drainFromShared`
- Heap.cpp:1502: `WTF::SharedTaskFunctor<void (...)::$_1>::run`
- ParallelHelperPool.cpp:110: `WTF::ParallelHelperPool::Thread::work`
- AutomaticThread.cpp:225: `WTF::Detail::CallableWrapper<WTF::AutomaticThread::start(...)::$_0, void>::call`
- Function.h:82: `WTF::wtfThreadEntryPoint`
Root Cause
The crash occurs during JavaScriptCore's parallel garbage collection when marking WebCore::DOMGCOutputConstraint objects. The PartyWebSocket class from the partysocket library creates WebCore/DOM objects that are destroyed while parallel GC threads are trying to mark them, causing the "pure virtual method called" error.
Key observation: The stack trace shows WebCore::DOMGCOutputConstraint, indicating this is related to DOM/WebCore objects, not just WebSocket connections. The partysocket library likely wraps or extends native WebSocket functionality in a way that creates additional WebCore objects that become invalid during GC.
Problematic Code
Before (Causing Crash)
// backend/apps/support/src/lib/chat-api-client.ts
import { WebSocket as PartyWebSocket } from 'partysocket'
// Create WebSocket connection using partysocket (with auto-reconnect)
let sharedWebSocket: PartyWebSocket | null = null
let sharedWsClient: ContractRouterClient<typeof chatContract> | null = null
function getSharedWebSocketClient(): ContractRouterClient<typeof chatContract> {
// Return existing client if WebSocket is open
if (
sharedWsClient &&
sharedWebSocket &&
sharedWebSocket.readyState === PartyWebSocket.OPEN
) {
return sharedWsClient
}
// Create new WebSocket connection with partysocket
// partysocket provides automatic reconnection and message buffering
sharedWebSocket = new PartyWebSocket(getWebSocketUrl()) // ❌ CRASHES HERE
// Create oRPC client from WebSocket
const link = new WSLink({
websocket: sharedWebSocket as any,
})
sharedWsClient = createORPCClient(link) as ContractRouterClient<
typeof chatContract
>
sharedWebSocket.addEventListener('close', () => {
sharedWsClient = null
})
return sharedWsClient
}
// Created at module load time - WebSocket created immediately
export const chatApiClient: ContractRouterClient<typeof chatContract> =
getSharedWebSocketClient() // ❌ Creates PartyWebSocket immediately
After (Fixed)
// backend/apps/support/src/lib/chat-api-client.ts
// REMOVED: import { WebSocket as PartyWebSocket } from 'partysocket'
// Using native WebSocket instead
// Create WebSocket connection using native WebSocket
let sharedWebSocket: WebSocket | null = null
let sharedWsClient: ContractRouterClient<typeof chatContract> | null = null
function getSharedWebSocketClient(): ContractRouterClient<typeof chatContract> {
// Return existing client if WebSocket is open
if (
sharedWsClient &&
sharedWebSocket &&
sharedWebSocket.readyState === WebSocket.OPEN // ✅ Native WebSocket
) {
return sharedWsClient
}
// Create new WebSocket connection with native WebSocket
// Using native WebSocket instead of PartyWebSocket to avoid GC crashes
sharedWebSocket = new WebSocket(getWebSocketUrl()) // ✅ Native WebSocket
// Create oRPC client from WebSocket
const link = new WSLink({
websocket: sharedWebSocket as any,
})
sharedWsClient = createORPCClient(link) as ContractRouterClient<
typeof chatContract
>
sharedWebSocket.addEventListener('close', () => {
sharedWsClient = null
})
return sharedWsClient
}
// Lazy-load chatApiClient to avoid creating WebSocket at module load time
let _chatApiClient: ContractRouterClient<typeof chatContract> | null = null
export const chatApiClient: ContractRouterClient<typeof chatContract> =
new Proxy({} as ContractRouterClient<typeof chatContract>, {
get(_target, prop) {
if (!_chatApiClient) {
_chatApiClient = getSharedWebSocketClient() // ✅ Lazy-loaded
}
return (_chatApiClient as any)[prop]
},
})
Workaround
Replace PartyWebSocket from partysocket with native WebSocket:
-
Remove partysocket import:
// REMOVED: import { WebSocket as PartyWebSocket } from 'partysocket' -
Use native WebSocket:
const websocket = new WebSocket(wsUrl) // Instead of new PartyWebSocket(wsUrl) -
Lazy-load WebSocket connections to avoid creating them at module load time:
// Use Proxy to lazy-load instead of creating at module load export const chatApiClient = new Proxy({}, { get(_target, prop) { if (!_chatApiClient) { _chatApiClient = getSharedWebSocketClient() } return (_chatApiClient as any)[prop] }, })
Related Issues
- Similar crash reported with worker threads: https://github.com/oven-sh/bun/issues/14559
- Both involve "pure virtual method called" during parallel GC marking
- Both occur after several minutes of runtime
Additional Context
The crash is not related to:
- Native
WebSocketusage (works fine) - Async iterators with native WebSocket
- EventPublisher or mergeIterators
- AbortSignal usage
The crash is specifically related to:
PartyWebSocketfrompartysocketlibrary- Objects created by
partysocketthat reference WebCore/DOM - These objects being destroyed during parallel GC marking
Expected Behavior
The application should run indefinitely without crashing. Native WebSocket works correctly, but PartyWebSocket causes crashes after 3-4 minutes.
Actual Behavior
Application crashes with C++ exception after 3-4 minutes when using PartyWebSocket. Replacing with native WebSocket resolves the issue.
Reproduction
- Create a WebSocket connection using
PartyWebSocketfrompartysocket - Keep the connection alive for 3-4 minutes
- Wait for JavaScriptCore's parallel GC to run
- Crash occurs when GC tries to mark WebCore objects created by
partysocket
Impact
- Severity: High - causes application crashes
- Frequency: 100% reproducible after 3-4 minutes
- Workaround: Use native
WebSocketinstead ofPartyWebSocket
- 主要言語
- TypeScript
- スター
- 1.3k
- フォーク
- 84
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
cloudflare/partykit のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
cloudflare/partykit#425 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
cloudflare/partykit#424 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
cloudflare/partykit#423 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
cloudflare/partykit#419 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
cloudflare/partykit#411 ·
cloudflare/partykit の issue をすべて見る
似ている issue
-
check:passed streams:add
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
S: triage
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
iot client
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
Open-Locker/Open-Locker#365 ·
メンテナーはふだん 1 日以内に返信
-
bug enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 69/100
inmanta/web-console#7787 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
shoelace-style/webawesome#2979 ·
メンテナーはふだん 1 日以内に返信