Repository navigation
fix: 🐛 Prevent nullptr == Tap() crash on iOS during recorder stream start - #496
Open
lavigarg-simform wants to merge 2 commits into
Open
lavigarg-simform wants to merge 2 commits into
lavigarg-simform wants to merge 2 commits into
Conversation
lavigarg-simform
marked this pull request as ready for review
June 19, 2026 15:01
vasu-nageshri-simform
left a comment
Contributor
There was a problem hiding this comment.
The fix addresses the root cause of #487. I reproduced the crash with a standalone AVAudioEngine script on macOS:
- Old pattern (
installTaptwice on bus 0 with no cleanup): aborts with*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: nullptr == Tap()' - PR pattern (
stop()if running +removeTap(onBus: 0)beforeinstallTap): second attach works, no crash removeTap(onBus: 0)on a bus with no tap is a no-op, so calling it unconditionally is safe- The example app builds for the iOS simulator on this branch (
flutter build ios --simulator --debug)
Realistic ways to hit a double attach() in the current code, all covered by this fix:
AudioRecorder(and its singleRecorderBytesStreamEngine) is one instance per plugin, so twoRecorderControllers recording, or a fast double tap onrecord()(the Dart state check runs before theawait), callsattach()twice on the same engine.- Hot restart: the native plugin instance survives while Dart state resets to
stopped, so the nextrecord()lands on an engine that still has the old tap. - A failed
audioEngine.start()left the tap installed. Fixed by theremoveTapincatch.
Inline comments below. Only the paused reset is something I'd like to see in this PR. The rest are nits or follow-ups.
General
- Heads-up: the pending SPM work (
feat/swift-package-manager-support) renames this file toios/audio_waveforms/Sources/audio_waveforms/RecorderBytesStreamEngine.swift. Whichever lands second needs a rebase. Git rename detection should handle most of it, but please double-check the change ends up in the new path. - macOS isn't affected: it creates a new
AVAudioEngineon each record and doesn't useRecorderBytesStreamEngine.
- Reset the paused flag in attach() and detach() so a recording started after record -> pause -> stop is not silent - Make attach() throw and let startRecording send a single result, and stop the file recorder when the engine fails to start - Fix indentation in the catch block - Link the PR in the CHANGELOG entry
vasu-nageshri-simform
force-pushed
the
fix/I487_ios_crash_audio_bytes
branch
from
October 29, 2026 04:28
745409c to
ed22b64
Compare
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.
Description
On iOS, starting the recorder bytes stream could crash with a fatal
nullptr == Tap()exception (issue #487).RecorderBytesStreamEngine.attach()installed a tap on input bus 0 withoutclearing any existing tap first. Two paths left a stale tap on the bus:
attach()call beforedetach()(re-start without teardown).audioEngine.start()that threw — the tap was installed but neverremoved, leaking it onto the bus.
AVAudioEnginethrowsnullptr == Tap()when a tap is installed on a bus thatalready has one, crashing the app.
This PR makes
attach()reset prior state before installing:totalFrames.audioEngine.start(), removes the just-installed tap so itdoesn't leak onto the bus and crash the next
attach().No public API change.
Checklist
fix:,feat:,docs:etc).docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues
Closes #487