AVAssetExportSession in iOS18- Thread 11: "*** -[AVAssetExportSession exportAsynchronouslyWithCompletionHandler:] Cannot call exportAsynchronouslyWithCompletionHandler: more than once."

I’m experiencing a crash at runtime when trying to extract audio from a video. This issue occurs on both iOS 18 and earlier versions. The crash is caused by the following error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[AVAssetExportSession exportAsynchronouslyWithCompletionHandler:] Cannot call exportAsynchronouslyWithCompletionHandler: more than once.' *** First throw call stack: (0x1875475ec 0x184ae1244 0x1994c49c0 0x217193358 0x217199899 0x192e208b9 0x217192fd9 0x30204c88d 0x3019e5155 0x301e5fb41 0x301af7add 0x301aff97d 0x301af888d 0x301aff27d 0x301ab5fa5 0x301ab6101 0x192e5ee39) libc++abi: terminating due to uncaught exception of type NSException My previous code worked fine, but it's crashing with Swift 6.

Does anyone know a solution for this?

## **Previous code:**


func extractAudioFromVideo(from videoURL: URL, exportHandler: ((AVAssetExportSession, CurrentValueSubject<Float, Never>?) -> Void)? = nil, completion: @escaping (Swift.Result<URL, Error>) -> Void) {
    let asset = AVAsset(url: videoURL)

    
    // Create an AVAssetExportSession to export the audio track
    guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
        completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create AVAssetExportSession"])))
        return
    }
    
    // Set the output file type and path
    guard let filename = videoURL.lastPathComponent.components(separatedBy: ["."]).first else { return  }
    let outputURL = VideoUtils.getTempAudioExportUrl(filename)
    VideoUtils.deleteFileIfExists(outputURL.path)
    exportSession.outputFileType = .m4a
    exportSession.outputURL = outputURL
    
    let audioExportProgressPublisher = CurrentValueSubject<Float, Never>(0.0)
    if let exportHandler = exportHandler {
        exportHandler(exportSession, audioExportProgressPublisher)
    }
    
    // Periodically check the progress of the export session
    let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
        audioExportProgressPublisher.send(exportSession.progress)
    }
    
    // Export the audio track asynchronously
    exportSession.exportAsynchronously {
        switch exportSession.status {
        case .completed:
            completion(.success(outputURL))
        case .failed:
            completion(.failure(exportSession.error ?? NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unknown error occurred while exporting audio"])))
        case .cancelled:
            completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Export session was cancelled"])))
        default:
            completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unknown export session status"])))
        }
        
        // Invalidate the timer when the export session completes or is cancelled
        timer.invalidate()
    }
}

## New Code: 


func extractAudioFromVideo(from videoURL: URL, exportHandler: ((AVAssetExportSession, CurrentValueSubject<Float, Never>?) -> Void)? = nil, completion: @escaping (Swift.Result<URL, Error>) -> Void) async {
	let asset = AVAsset(url: videoURL)
	
	// Create an AVAssetExportSession to export the audio track
	guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
		completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create AVAssetExportSession"])))
		return
	}
	
	// Set the output file type and path
	guard let filename = videoURL.lastPathComponent.components(separatedBy: ["."]).first else { return  }
	let outputURL = VideoUtils.getTempAudioExportUrl(filename)
	VideoUtils.deleteFileIfExists(outputURL.path)
	
	
	let audioExportProgressPublisher = CurrentValueSubject<Float, Never>(0.0)
	if let exportHandler {
		exportHandler(exportSession, audioExportProgressPublisher)
	}
	
	if #available(iOS 18.0, *) {
		do {
			try await exportSession.export(to: outputURL, as: .m4a)
			
			let states = exportSession.states(updateInterval: 0.1)
			for await state in states {
				switch state {
				case .pending, .waiting:
					break
				case .exporting(progress: let progress):
					print("Exporting: \(progress.fractionCompleted)")
					if progress.isFinished {
						completion(.success(outputURL))
					}else if progress.isCancelled {
						completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Export session was cancelled"])))
					}else {
						audioExportProgressPublisher.send(Float(progress.fractionCompleted))
					}
				}
			}
		}catch let error {
			print(error.localizedDescription)
		}
	}else {
		// Periodically check the progress of the export session
		let publishTimer = Timer.publish(every: 0.1, on: .main, in: .common)
			.autoconnect()
			.sink { [weak exportSession] _ in
				guard let exportSession else { return }
				audioExportProgressPublisher.send(exportSession.progress)
			}
		exportSession.outputFileType = .m4a
		exportSession.outputURL = outputURL
		await exportSession.export()
		
		switch exportSession.status {
		case .completed:
			completion(.success(outputURL))
		case .failed:
			completion(.failure(exportSession.error ?? NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unknown error occurred while exporting audio"])))
		case .cancelled:
			completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Export session was cancelled"])))
		default:
			completion(.failure(NSError(domain: "com.example.app", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unknown export session status"])))
		}
		
		// Invalidate the timer when the export session completes or is cancelled
		publishTimer.cancel()
	}
}

Hello @raju9586,

Are you sure that your code doesn't result in multiple calls to export() on the same export session? Try setting a symbolic breakpoint for exportAsynchronouslyWithCompletionHandler.

-- Greg

AVAssetExportSession in iOS18- Thread 11: "*** -[AVAssetExportSession exportAsynchronouslyWithCompletionHandler:] Cannot call exportAsynchronouslyWithCompletionHandler: more than once."
 
 
Q