Skip to content

Fix DebugEventLog NSFileHandle ObjC exception crash - #3034

Merged
austinywang merged 2 commits into
mainfrom
issue-1924-debug-event-log-crash
Apr 20, 2026
Merged

austinywang merged 2 commits into
mainfrom
issue-1924-debug-event-log-crash

Conversation

@austinywang

@austinywang austinywang commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1924.

Depends on the companion bonsplit PR: manaflow-ai/bonsplit#99

Problem

DebugEventLog.log(_:) (and three mirror call sites in the outer repo) used the deprecated Objective-C-style FileHandle APIs: seekToEndOfFile(), write(_:), and closeFile(). When those fail (log file unlinked, disk full, permission issue, etc.) they raise NSFileHandleOperationException. Objective-C exceptions are not caught by Swift do/catch, so the exception propagates out of the Swift runtime and the uncaught-exception handler calls abort(). That is the crash reported in #1924.

Fix

Switch every call site to the Swift-throwing replacements and guard appropriately:

  • try? handle.seekToEnd() (not seekToEndOfFile())
  • try? handle.write(contentsOf: data) (not write(_:))
  • try? handle.close() in defer (not closeFile())
  • guard (try? handle.seekToEnd()) != nil else { return } so we never blindly append at offset 0 when the seek throws

These APIs throw Swift errors instead of raising ObjC exceptions, so a transient log-write failure silently drops the entry instead of crashing the app. Changes stay inside the existing #if DEBUG guards.

Why this replaces #1931

#1931 from an external contributor only touched the two outer-repo files and stalled on rebase. It did not fix the actual root crash site at vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift (the dlog(...) implementation), which is where the most frequent crash path originates.

Changed call sites

  1. vendor/bonsplit submodule bump — Sources/Bonsplit/Public/DebugEventLog.swift (root cause, picks up the fix from the bonsplit PR)
  2. Sources/GhosttyTerminalView.swift — three call sites: initLog, surfaceLog, sizeLog
  3. Sources/WorkspaceContentView.swift — one call site: debugPanelLookup

Note

Low Risk
Changes are limited to #if DEBUG file logging and primarily swap APIs with defensive error handling; production behavior should be unaffected.

Overview
Prevents DEBUG-only logging from crashing the app by replacing deprecated Objective-C FileHandle calls (seekToEndOfFile(), write(_:), closeFile()) with Swift-throwing equivalents (seekToEnd(), write(contentsOf:), close() in defer) in GhosttyTerminalView and WorkspaceContentView.

Log appends now guard against seek failures (dropping the log entry rather than writing at offset 0 / triggering an uncaught ObjC exception) while preserving existing log-file creation behavior.

Reviewed by Cursor Bugbot for commit 2b65623. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Prevent a crash in debug logging caused by ObjC NSFileHandle exceptions by switching all debug log writes to Swift-throwing APIs and guarding seek failures, including the background log writer. Fixes #1924.

  • Bug Fixes
    • Replace seekToEndOfFile()/write(_:)/closeFile() with try? seekToEnd(), try? write(contentsOf:), and defer { try? close() }.
    • Guard seek failures and drop the entry on error instead of crashing; remains within #if DEBUG.
    • Update call sites: four in Sources/GhosttyTerminalView.swift (initLog, background log, surfaceLog, sizeLog) and one in Sources/WorkspaceContentView.swift (debugPanelLookup).
    • Bump vendor/bonsplit to include the DebugEventLog fix.

Written for commit 2b65623. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Improved debug log file handling to prevent resource leaks and crashes: safer open/append logic, guarded early-return when appending fails, and ensured handles are closed; preserves existing fallback file creation.
  • Chores

    • Updated a vendored dependency to a newer revision.

@vercel

vercel Bot commented Apr 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 20, 2026 6:48am

@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91c96237-b178-4eaf-943d-830dab894839

📥 Commits

Reviewing files that changed from the base of the PR and between 3d655b5 and 2b65623.

📒 Files selected for processing (1)
  • Sources/GhosttyTerminalView.swift

📝 Walkthrough

Walkthrough

Two Swift source files received updates to debug logging operations, replacing manual file-handle management with safer defer-based cleanup patterns and guarded seeks. Additionally, the vendor/bonsplit submodule pointer was advanced to a newer commit.

Changes

Cohort / File(s) Summary
Debug Logging Safety
Sources/GhosttyTerminalView.swift, Sources/WorkspaceContentView.swift
Replaced manual seekToEndOfFile(), write(...), and closeFile() with defer { try? handle.close() }, guarded seekToEnd() checks that early-return on failure, and try? handle.write(contentsOf:) usage.
Vendored Dependency
vendor/bonsplit
Updated submodule pointer from 73bd9f9 to cffd9a6.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 A safer path the logs now take,
Defer to close, no handles break,
Seeks are guarded, writes are mild,
Bonsplit moved along, compiled,
Hop, nibble, code — a tidy trail.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: fixing an NSFileHandle crash in DebugEventLog by replacing deprecated Objective-C exception-raising APIs.
Description check ✅ Passed The description comprehensively covers the problem (ObjC exceptions from deprecated FileHandle APIs), the fix (switching to Swift-throwing equivalents), and all changed call sites, providing clear rationale and context.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-1924-debug-event-log-crash

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vendor/bonsplit`:
- Line 1: The vendor/bonsplit submodule pointer was updated to commit
cffd9a66e9ede9aaedefcfd0c391f0ad49652d72 which only exists on the feature branch
fix-debug-event-log-nsfilehandle-crash and depends on manaflow-ai/bonsplit#99;
revert the pointer in vendor/bonsplit to the current bonsplit main commit
(1bfb226) or restore the previous submodule state, and do not update the pointer
to cffd9a66... until the companion PR manaflow-ai/bonsplit#99 is merged and that
commit is present on bonsplit main; once the PR is merged, update the submodule
pointer to the new main commit and re-run submodule init/update and verify CI
before merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f18cc92-2a5f-442d-a792-7e38b38a1962

📥 Commits

Reviewing files that changed from the base of the PR and between 3af1173 and 3d655b5.

📒 Files selected for processing (3)
  • Sources/GhosttyTerminalView.swift
  • Sources/WorkspaceContentView.swift
  • vendor/bonsplit

Comment thread vendor/bonsplit
@@ -1 +1 @@
Subproject commit 73bd9f9cfd8c41ebc6f2d43eb48d3eafa4dfec00
Subproject commit cffd9a66e9ede9aaedefcfd0c391f0ad49652d72

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that bonsplit companion PR has been merged and commit is on main branch

echo "=== Checking companion PR status ==="
gh pr view 99 --repo manaflow-ai/bonsplit --json state,merged,mergedAt,headRefName

echo ""
echo "=== Verifying commit cffd9a66e9ede9aaedefcfd0c391f0ad49652d72 is on main branch ==="
cd vendor/bonsplit
git fetch origin main
git branch -r --contains cffd9a66e9ede9aaedefcfd0c391f0ad49652d72 | grep 'origin/main'

if [ $? -eq 0 ]; then
    echo "✓ Commit is on origin/main"
else
    echo "✗ WARNING: Commit is NOT on origin/main"
    exit 1
fi

Repository: manaflow-ai/cmux

Length of output: 1212


🏁 Script executed:

#!/bin/bash
# Verify companion PR status and commit location with corrected syntax

echo "=== Checking companion PR status ==="
gh pr view 99 --repo manaflow-ai/bonsplit --json state,mergedAt,headRefName,number 2>/dev/null || echo "Could not fetch PR info"

echo ""
echo "=== Checking if commit exists in bonsplit repo ==="
cd vendor/bonsplit
git fetch origin 2>&1 | head -20

echo ""
echo "=== Listing all branches containing or referencing the commit ==="
git branch -a -v | grep -E "(main|master|cffd9a66)" || echo "Commit not found in local branches"

echo ""
echo "=== Checking git log for the specific commit ==="
git log --oneline -1 cffd9a66e9ede9aaedefcfd0c391f0ad49652d72 2>/dev/null || echo "Commit not found in local repo history"

echo ""
echo "=== Checking current state of bonsplit submodule ==="
cd - > /dev/null
git submodule status vendor/bonsplit

Repository: manaflow-ai/cmux

Length of output: 776


Do not merge until the companion bonsplit PR is merged and commit is on main branch.

The submodule pointer references commit cffd9a66e9ede9aaedefcfd0c391f0ad49652d72, which:

Per the submodule workflow requirement, the companion PR must be merged and the commit pushed to main in the bonsplit repository before updating the pointer in cmux. Update the submodule pointer only after manaflow-ai/bonsplit#99 is merged.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vendor/bonsplit` at line 1, The vendor/bonsplit submodule pointer was updated
to commit cffd9a66e9ede9aaedefcfd0c391f0ad49652d72 which only exists on the
feature branch fix-debug-event-log-nsfilehandle-crash and depends on
manaflow-ai/bonsplit#99; revert the pointer in vendor/bonsplit to the current
bonsplit main commit (1bfb226) or restore the previous submodule state, and do
not update the pointer to cffd9a66... until the companion PR
manaflow-ai/bonsplit#99 is merged and that commit is present on bonsplit main;
once the PR is merged, update the submodule pointer to the new main commit and
re-run submodule init/update and verify CI before merging.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 3 files

@greptile-apps

greptile-apps Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Replaces ObjC-exception-raising FileHandle APIs (seekToEndOfFile(), write(_:), closeFile()) with their Swift-throwing equivalents across four debug-log call sites in GhosttyTerminalView.swift, WorkspaceContentView.swift, and the vendor/bonsplit submodule, fixing the NSFileHandleOperationException → abort() crash (#1924). The migrated functions correctly use defer { try? handle.close() }, guard (try? handle.seekToEnd()) != nil else { return }, and try? handle.write(contentsOf:).

  • logBackground (line 3475, not listed in the PR's scope) uses try? handle.seekToEnd() without the guard introduced elsewhere — a seek failure would cause the subsequent write to run at offset 0, overwriting existing log content.
  • The vendor/bonsplit pointer must point to a commit already on bonsplit main per the project's submodule safety policy; verify with git merge-base --is-ancestor HEAD origin/main before landing.

Confidence Score: 4/5

Safe to merge once the companion bonsplit PR is confirmed merged to main and the missing seek guard is addressed.

The core crash fix is sound and consistently applied to all four documented call sites. Two concerns block a 5: the logBackground missing guard (pre-existing path not updated by this PR) and the submodule safety requirement per CLAUDE.md that needs explicit verification before landing.

vendor/bonsplit (submodule merge-base check) and Sources/GhosttyTerminalView.swift line 3475 (logBackground seek guard).

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Three debug log helpers (initLog, surfaceLog, sizeLog) correctly migrated to Swift-throwing FileHandle APIs with the guard-on-seek pattern; logBackground (not listed in PR scope) is missing the same guard and would overwrite offset 0 on a seek failure.
Sources/WorkspaceContentView.swift debugPanelLookup correctly migrated to Swift-throwing APIs with defer close, guard-on-seek, and write(contentsOf:); Release no-op stub unchanged.
vendor/bonsplit Submodule pointer bumped to pick up the DebugEventLog.swift fix from bonsplit PR #99; depends on that PR being merged to bonsplit main before this pointer is safe to land.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[log function called] --> B{File exists at path?}
    B -- No --> C[FileManager.createFile]
    B -- Yes --> D[FileHandle forWritingAtPath / forWritingTo]
    D --> E[defer: try? handle.close]
    E --> F{try? handle.seekToEnd != nil?}
    F -- throws/nil --> G[return — no write]
    F -- OK / UInt64 --> H[try? handle.write contentsOf data]
    H --> I[defer fires: close]

    style G fill:#f96,color:#000
    style H fill:#6f6,color:#000
    style C fill:#adf,color:#000
Loading

Comments Outside Diff (1)

  1. Sources/GhosttyTerminalView.swift, line 3475 (link)

    P2 Missing guard on seek in logBackground

    logBackground calls try? handle.seekToEnd() without the guard that was added to every other migrated function in this PR. If the seek throws, try? returns nil, execution falls through, and write(contentsOf:) runs at offset 0 — overwriting the start of the log file with the new entry. The PR description explicitly calls out this guard as the fix for "we never blindly append at offset 0 when the seek throws".

Reviews (1): Last reviewed commit: "Fix NSFileHandle ObjC exception crash in..." | Re-trigger Greptile

Comment thread vendor/bonsplit
@@ -1 +1 @@
Subproject commit 73bd9f9cfd8c41ebc6f2d43eb48d3eafa4dfec00
Subproject commit cffd9a66e9ede9aaedefcfd0c391f0ad49652d72

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Submodule pointer may precede companion PR merge

Per the project's submodule safety policy (CLAUDE.md): the submodule commit must be reachable from origin/main in the bonsplit repo before the parent pointer is committed here. The PR description says this PR depends on bonsplit PR #99 being merged first. Before merging, please verify:

cd vendor/bonsplit && git merge-base --is-ancestor HEAD origin/main

If that exits non-zero, the bonsplit commit isn't on main yet and will be orphaned once the feature branch is deleted.

@austinywang
austinywang merged commit 92769d7 into main Apr 20, 2026
15 of 17 checks passed
rodchristiansen pushed a commit to rodchristiansen/cmux that referenced this pull request Sep 2, 2026
* Fix NSFileHandle ObjC exception crash in debug logging

* Guard logBackground seek to avoid writing at offset 0 on failure

This branch was successfully deployed

1 active deployment
Preview — 2b65623b Deployed Apr 20, 2026 by vercel[bot]
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.

Crash in DebugEventLog.log: uncaught NSFileHandle ObjC exception

1 participant