fix: wrap insertEvent with withRetry for concurrent PostToolUse hooks#243
Open
james-gough-op wants to merge 1 commit intomksglu:mainfrom
Open
Conversation
When many tool calls complete in parallel (e.g., batch Linear get_issue), Claude Code spawns multiple PostToolUse hooks simultaneously. These all open the same per-project SessionDB and compete for the SQLite write lock. While better-sqlite3's busy_timeout (30s) handles most contention, edge cases during transaction lock escalation can still surface SQLITE_BUSY. This causes hooks to fail, and Claude Code reports "hook error" to users. Changes: - Wrap SessionDB.insertEvent() transaction with withRetry() for defense-in-depth against SQLITE_BUSY under concurrent hook access - Set busy_timeout pragma in BunSQLiteAdapter to match better-sqlite3's timeout option (previously ignored, causing immediate SQLITE_BUSY failures under Bun runtime) - Add concurrent insert test verifying multiple DB instances can write to the same file without data loss
18 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / Why / How
What: Wrap
SessionDB.insertEvent()withwithRetry()and propagatebusy_timeoutpragma to Bun's SQLite adapter.Why: When many tool calls complete in parallel (e.g., 16 batch
mcp__linear__get_issuecalls during a standup summary), Claude Code spawns multiple PostToolUse hooks simultaneously. These all open the same per-project SessionDB and compete for the SQLite write lock. While better-sqlite3'sbusy_timeout(30s) handles most contention, edge cases during transaction lock escalation can surfaceSQLITE_BUSY, causing Claude Code to reportPostToolUse:mcp__linear__get_issue hook errorto users.Additionally, the
BunSQLiteAdapterfactory was silently dropping thetimeoutoption passed fromSQLiteBase, meaning Bun runtime had nobusy_timeoutat all — concurrent writes would fail immediately withSQLITE_BUSY.How:
src/session/db.ts— Changedtransaction()tothis.withRetry(() => transaction())ininsertEvent, leveraging the existing retry infrastructure inSQLiteBasesrc/db-base.ts— Addedbusy_timeoutpragma inBunDatabaseFactorywhenopts.timeoutis provided, matching better-sqlite3's automatic behaviorAffected platforms
(The Bun
busy_timeoutfix applies to any platform using Bun runtime for the MCP server. ThewithRetryfix applies to all platforms since PostToolUse hooks run with Node.js.)Test plan
Concurrent Insert Resiliencetest intests/session/session-db.test.ts— opens 5SessionDBinstances against the same DB file and inserts events from each, verifying all 5 events are stored without errorsnpm run typecheckpasses cleanChecklist
npm testpasses (6 pre-existing failures unrelated to this change — security, cursor-hooks, vscode-hooks, session-hooks-smoke, hooks/integration)npm run typecheckpassesnextbranch (unless hotfix)