Description
**Crash report**: 94AF43D3-67EF-4948-B897-ABCDF91EACEC
**Version**: 0.2.115 (142), TestFlight, iPad17,4, iOS 26.3
**Date**: 2026-05-20 16:56
**Root cause**: `MicrophoneRelay.startAsync(bodyHost:)` calls `input.installTap(onBus:bufferSize:format:block:)` with no guard against a tap already being installed. `AVAudioNode.installTap` throws an Objective-C NSException when a tap is already on bus 0 — this cannot be caught with Swift `do-catch`, so it aborts the process.
The `start()` method has no early-exit if `isRunning == true`, so a second call (e.g., on reconnect or stream restart) races to install a second tap and crashes.
**Stack trace (top frames)**:
```
AVFAudio AVAudioEngineImpl::InstallTapOnNode + 1376
AVFAudio -[AVAudioNode installTapOnBus:bufferSize:format:block:] + 564
HydraHeadiPad MicrophoneRelay.startAsync(bodyHost:) + 704 (MicrophoneRelay.swift:51)
```
**Fix (MicrophoneRelay.swift)**:
1. Add `guard !isRunning else { return }` at the top of `startAsync` to prevent double-start.
2. Call `input.removeTap(onBus: 0)` before `installTap` as a defensive belt-and-suspenders measure.