DEV Community

Todd Sullivan
Todd Sullivan

Posted on

The AI Coding Agent Passed the Build. The App Still Crashed on Launch

I had Claude co-author an adaptive audio feature for a small Swift game.

The code compiled. The tests passed. The app installed. Then it crashed as soon as the audio engine called its render block.

There was no useful exception message, just SIGTRAP.

The problem was where the closure had been created. The audio class was @MainActor, so Swift 6 treated the render closure as main-actor isolated too:

let node = AVAudioSourceNode(format: format) { _, _, frames, buffers in
    synth.render(frames: Int(frames), into: buffers)
    return noErr
}
Enter fullscreen mode Exit fullscreen mode

That closure does not run on the main actor. Core Audio calls it from a real-time audio thread. The compiler accepted the handoff, but the runtime actor check trapped the first time Core Audio invoked it.

The fix was small: form the callback outside the actor-isolated context and make the threading contract explicit.

nonisolated private static func sourceNode(
    format: AVAudioFormat,
    synth: MusicSynth
) -> AVAudioSourceNode {
    AVAudioSourceNode(format: format) { @Sendable _, _, frames, buffers in
        synth.render(frames: Int(frames), into: buffers)
        return noErr
    }
}
Enter fullscreen mode Exit fullscreen mode

The more useful fix was to the workflow.

I had treated a clean build as evidence that the agent-written change worked. It was only evidence that the types lined up. The failure lived at a runtime boundary: framework callback, different thread, stricter Swift 6 isolation.

Now any AI-assisted change that hands a closure to a system framework gets a tiny runtime gate:

  • launch the app in the simulator
  • exercise the callback path
  • leave it running for 60 seconds
  • only then install or ship the build

This is not an argument against coding agents. Claude helped diagnose and fix the crash as well. It is an argument for testing the boundary the compiler cannot prove.

AI-generated code is especially good at looking locally correct. Framework callbacks fail globally: actor isolation, lifecycle, permissions, background execution, hardware state.

The lazy guardrail is not another review checklist. It is one runnable smoke test that crosses the real boundary.

Top comments (1)

Collapse
 
oddeggs profile image
Odd Eggs Studio •

"A clean build only proves the types lined up" is the line I'm keeping. Closures a framework calls from its own thread are exactly where Swift 6 isolation bites, so a 60-second run on the real path is cheap insurance.