Skip to content

fix(audio): guard against invalid inputNode format after fresh mic permission grant#81

Merged
missuo merged 1 commit into
missuo:mainfrom
thedavidweng:cursor/fix-audio-10877-ab4a
Apr 13, 2026
Merged

fix(audio): guard against invalid inputNode format after fresh mic permission grant#81
missuo merged 1 commit into
missuo:mainfrom
thedavidweng:cursor/fix-audio-10877-ab4a

Conversation

@thedavidweng

Copy link
Copy Markdown
Contributor

Summary

Fix -10877 (kAudioUnitErr_InvalidElement) crash that occurs when a user triggers voice input for the first time immediately after granting microphone permission.

Root Cause

After a fresh microphone permission grant, AVAudioEngine.inputNode may temporarily report 0 channels / 0 sampleRate while the macOS audio subsystem reconfigures. The previous code proceeded unconditionally with this invalid format:

AVAudioFormat *hardwareFormat = [inputNode outputFormatForBus:0];
// channelCount=0, sampleRate=0 — no validation

[inputNode installTapOnBus:0 bufferSize:4096 format:hardwareFormat block:...];
[self.audioEngine startAndReturnError:&error]; // throws -10877

AVAudioEngine.start() then fails with -10877 because the Audio Unit has no valid element/connection on bus 0. The user sees this as "pressing the trigger key does nothing" with throwing -10877 flooding the Console log.

Reproduction

  1. Fresh install Koe v1.0.14 (build 15)
  2. Launch → grant Microphone permission when prompted
  3. Immediately press the trigger key (Fn)
  4. Console shows multiple throwing -10877 entries

Changes

SPAudioCaptureManager.m

  • Add format validation after querying outputFormatForBus:0 — return NO early if channelCount == 0 or sampleRate <= 0, with a descriptive log message

SPAppDelegate.m

  • Extract duplicated audio-capture-start code (hold + tap paths) into startAudioCaptureWithRetry
  • On first capture failure, retry once after 500ms to allow the audio subsystem time to settle after a permission change
  • Retry is session-token-guarded to avoid stale execution if the user triggers a new session during the delay

Behavior

Scenario Before After
First use after granting mic permission -10877 crash, no audio Retry after 500ms, capture succeeds
Mic permission already granted Works Works (no retry needed, first attempt succeeds)
Mic permission denied Error cue, no explanation Error cue (unchanged; permission alert is a separate PR)
Bluetooth device reconnect mid-session Works (existing recovery) Works (unchanged)

Testing

  • All Rust tests pass (cargo test --no-default-features — 44+11+1 tests)
  • Modified .m files verified for balanced braces/brackets/parens
  • cargo fmt --check / cargo clippy — pass

…rmission grant

After a first-time microphone permission grant, AVAudioEngine.inputNode
may report 0 channels / 0 sampleRate while the audio subsystem
reconfigures. Proceeding with this invalid format causes
audioEngine.start() to throw -10877 (kAudioUnitErr_InvalidElement).

- Validate hardwareFormat.channelCount and sampleRate before installing
  the tap; return NO early with a descriptive log if invalid
- Add a single 500ms retry in SPAppDelegate when audio capture fails,
  giving the audio subsystem time to settle after permission changes
- Extract shared capture-start logic into startAudioCaptureWithRetry to
  avoid duplication between hold and tap code paths

Co-authored-by: Davy <thedavidweng@users.noreply.github.com>
@missuo missuo merged commit 1ab5516 into missuo:main Apr 13, 2026
@thedavidweng thedavidweng deleted the cursor/fix-audio-10877-ab4a branch April 13, 2026 07:53
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.

3 participants