Skip to content

Debug build crashes on Call Link end: owsFailDebug fires when AVAudioSession.setCategory throws during call teardown #6246

Description

@timomak
  • I have searched open and closed issues for duplicates
  • I am submitting a bug report for existing functionality that does not work as intended
  • This isn't a feature request or a discussion topic

Bug description

Ending a Call Link by tapping the red end-call button reproducibly crashes the app in Debug builds. The crash is an owsFailDebug (fatalError in Debug, no-op in Release) inside CallAudioService.setAudioSession(category:mode:options:), triggered when AVAudioSession.setCategory throws during call teardown.

This affects Debug builds only. Release builds (TestFlight / App Store) catch the same throw and log it without crashing, so end users on production builds do not see this. It does, however, reproducibly crash any contributor running a Debug build the moment they end a Call Link, which makes development of any call-related code on a real device awkward.

Reproduces every time, on both an iPhone 14 Pro (iOS 26.1) and an iPad Pro 12.9" 5th-gen / M1 (iPadOS 26.4).

Steps to reproduce

  • Build and run the Signal scheme in Debug on a real iOS device
  • Open or create a Call Link and join the call
  • Tap the red end-call button

Actual result: App crashes with EXC_BREAKPOINT / SIGTRAP from owsFailDebug inside CallAudioService.setAudioSession.

Expected result: The Call Link ends and the user returns to the chat list without crash. (The audio-session change failure during teardown is benign — the call is ending anyway — and should be logged at warn/error level, not fatal-asserted.)

Stack trace (top frames, faulting thread)

0  Signal  _assertionFailure(_:_:file:line:flags:)
1  Signal  fatalError(_:file:line:)
2  Signal  assertionFailure(_:file:line:)
3  Signal  owsFailDebug(_:logger:file:function:line:)
4  Signal  CallAudioService.setAudioSession(category:mode:options:)
5  Signal  CallAudioService.ensureProperAudioSession(call:)
6  Signal  CallAudioService.groupCallLocalDeviceStateChanged(_:)
7  Signal  protocol witness for GroupCallObserver.groupCallLocalDeviceStateChanged(_:)
8  Signal  closure #1 in GroupCall.groupCall(onLocalDeviceStateChanged:)
9  Signal  Sequence.forEach(_:)
10 Signal  GroupCall.groupCall(onLocalDeviceStateChanged:)
11 Signal  protocol witness for GroupCallDelegate.groupCall(onLocalDeviceStateChanged:)
12 Signal  GroupCall.handleJoinStateChanged(joinState:demuxId:)
13 Signal  closure #1 in CallManager.handleJoinStateChanged(clientId:joinState:demuxId:)

Root cause

Signal/Calls/CallAudioService.swift (around the setAudioSession private method) catches a throw from AVAudioSession.setCategory and reports it via owsFailDebug:

do {
    try avAudioSession.setCategory(category, mode: mode, options: options)
    Logger.info("AVAudioSession changed from [...] to [...]")
} catch {
    let message = "AVAudioSession failed to change from [...] to [...] with error: \(error)"
    owsFailDebug(message)
}

When the user ends a Call Link, RingRTC fires handleJoinStateChanged (the user is leaving the call), which propagates to CallAudioService.groupCallLocalDeviceStateChanged → ensureProperAudioSession → setAudioSession. At that moment iOS is concurrently deactivating the audio session as part of the call ending, so setCategory throws, and owsFailDebug turns the throw into a fatal assertion in Debug.

Suggested handling

The catch site is the right place to address this. A couple of options:

  1. Downgrade the catch from owsFailDebug to Logger.warn (or Logger.error) — the call is ending anyway, so the audio session not updating to the new category is not a meaningful failure mode worth a fatal assertion.
  2. Detect the call-teardown timing window and skip the setCategory attempt, or special-case the specific NSError domain/code that iOS returns when the session is being deactivated.

(1) is the smaller change.

Screenshots

n/a — backtrace-only crash.

Device info

Reproducible on both devices in Debug builds:

Device: iPhone 14 Pro
iOS version: 26.1
Signal version: 8.9 (locally built, Debug)

Device: iPad Pro 12.9" (5th generation, M1)
iOS version: iPadOS 26.4
Signal version: 8.9 (locally built, Debug)

Link to debug log

n/a — this is a developer-only crash that fires before the in-app "Submit Debug Log" flow can be exercised. Stack trace from the .ips crash report is included above; the full crash report can be supplied via email per the bug-report template instructions if it would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions