Draft
Fix recurring EXC_BAD_ACCESS crashes on hub disconnect#13
Conversation
- HotkeyManager: add deinit to disable/invalidate CGEvent tap before object is released; prevents dangling unretained C callback pointer (KERN_INVALID_ADDRESS when hub-triggered key events fire after dealloc) - AudioRecorder: add deinit to removeObserver/stop/removeTap; prevents @objc selector being called on deallocated object after hardware change; fix whisperFormat force-unwrap; guarantee onRecordingComplete on main - RecordingIndicator: add @mainactor; add init/deinit for screen-change observer (NSApplication.didChangeScreenParametersNotification) so the floating panel is repositioned when monitors connect/disconnect rather than being stranded off-screen; add deinit to dismiss panel - WhisperTranscriber: dispatch @published mutations to main thread; add alreadyTranscribing reentrancy guard and explicit main-thread flag cleanup (await MainActor.run) to prevent concurrent whisper.cpp inference and off-main @published data races - AppState: guard startRecording against !isTranscribing; guard switchModel against !isRecording && !isTranscribing; add early reentrancy guard to transcribeAndInject to safely handle hardware disconnect mid-inference without corrupting isTranscribing state - ModelManager: replace force-unwrap .first! with safe fallback; replace URL(string:)! with fatalError guard for programmer errors - UpdateChecker: use optional URL? for apiURL with explicit nil guard Agent-Logs-Url: https://github.com/ygivenx/freeWispr/sessions/8f0c2fd0-fc30-4bc7-80f4-510241df67b1 Co-authored-by: ygivenx <31292443+ygivenx@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix multiple crashes in FreeWispr application
Fix recurring EXC_BAD_ACCESS crashes on hub disconnect
Mar 27, 2026
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.
Nine crashes in one day, all
EXC_BAD_ACCESS / SIGSEGV / KERN_INVALID_ADDRESSin SwiftUI'sAttributeGraph/ViewBodyAccessor.updateBody, consistently triggered by unplugging a hub that carries both microphone and monitors during a Teams call.Critical: Dangling object references after deallocation
HotkeyManager— unretained C callback pointerCGEvent.tapCreatestoresUnmanaged.passUnretained(self)in the callback'suserInfo. Nodeinitexisted to invalidate the tap, so any key event after dealloc dereferences a freed pointer.AudioRecorder— orphanedAVAudioEngineConfigurationChangeobserverprepareEngine()registers an@objcselector but nodeinitremoved it. Hardware-change notifications after dealloc invoke the selector on freed memory. AddeddeinitcallingNotificationCenter.default.removeObserver(self)+ engine/tap teardown.High: RecordingIndicator stranded on disconnected screen
RecordingIndicatorhad no@MainActor, no cleanup, and no screen-change handling. When the external monitor disconnects the floatingNSPanelis left at off-screen coordinates with no repositioning path. Fixed:@MainActorannotation enforcing AppKit-on-main-threadNSApplication.didChangeScreenParametersNotification; repositions panel to current main screen on each changedeinitdismisses the panel and removes the observerMedium: Off-main
@Publishedmutations causing SwiftUI attribute graph racesWhisperTranscriber.transcribe()is a non-isolatedasyncfunction;isTranscribing = true/falseandisModelLoadedwere being set on the cooperative thread pool, racing with SwiftUI'sobjectWillChangesubscription. All@Publishedmutations now useawait MainActor.run {}/DispatchQueue.main.async.Also added a reentrancy guard (
alreadyTranscribing) to prevent two concurrent whisper.cpp inference sessions from corrupting the KV-cache context.Medium: AppState state machine gaps during concurrent hardware events
When mic+monitor disconnect simultaneously,
AVAudioEngineConfigurationChangecan fire while whisper is mid-inference (main actor suspended atawait). The resulting secondtranscribeAndInjecttask was clearingisTranscribingprematurely, collapsing the reentrancy protection. Also,startRecording()only guarded on!isRecording, not!isTranscribing, andswitchModel()could calltranscriber.unloadModel()mid-inference.startRecording():guard !isRecording, !isTranscribingswitchModel():guard !isSwitchingModel, !isRecording, !isTranscribingtranscribeAndInject(): early bail ifisTranscribingalready trueLow: Force unwraps
ModelManager:FileManager.urls(...).first!→guard letwithtemporaryDirectoryfallback;URL(string:)!→fatalErrorguard (programmer error, compile-time constant)AudioRecorder:AVAudioFormat(...)!→fatalErrorguard with descriptive messageUpdateChecker:apiURLchanged toURL?with explicit nil guard before network call💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.