Skip to content

Commit 5a3fbc2

Browse files
committed
Fix transcription hanging indefinitely when Groq stalls after TCP accept
URLSession's timeoutInterval does not reliably fire when a server accepts the TCP connection but then stops responding before sending any data. In that situation the upload call in transcribeAudio() hangs forever, blocking the entire recording pipeline with no user feedback and no way to recover short of force-quitting the app. PostProcessingService already avoids this by racing the API call against a Task.sleep timer inside a withThrowingTaskGroup — whichever finishes first wins, and the loser is cancelled. Apply the same pattern to TranscriptionService.transcribe() so the timeout is enforced by Swift concurrency rather than URLSession heuristics. The timeout duration is unchanged (transcriptionTimeoutSeconds, default 20 s). The error thrown is the existing TranscriptionError.transcriptionTimedOut.
1 parent e25b45b commit 5a3fbc2

1 file changed

Lines changed: 24 additions & 4 deletions

File tree

Sources/TranscriptionService.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,30 @@ class TranscriptionService {
5353
throw CancellationError()
5454
}
5555

56-
do {
57-
return try await transcribeAudio(fileURL: fileURL)
58-
} catch let urlError as URLError where urlError.code == .timedOut {
59-
throw TranscriptionError.transcriptionTimedOut(transcriptionTimeoutSeconds)
56+
let timeoutSeconds = transcriptionTimeoutSeconds
57+
return try await withThrowingTaskGroup(of: String.self) { group in
58+
group.addTask { [weak self] in
59+
guard let self else {
60+
throw TranscriptionError.transcriptionFailed("Transcription service deallocated")
61+
}
62+
return try await self.transcribeAudio(fileURL: fileURL)
63+
}
64+
65+
group.addTask {
66+
try await Task.sleep(nanoseconds: UInt64(timeoutSeconds * 1_000_000_000))
67+
throw TranscriptionError.transcriptionTimedOut(timeoutSeconds)
68+
}
69+
70+
do {
71+
guard let result = try await group.next() else {
72+
throw TranscriptionError.transcriptionFailed("No transcription result")
73+
}
74+
group.cancelAll()
75+
return result
76+
} catch {
77+
group.cancelAll()
78+
throw error
79+
}
6080
}
6181
}
6282

0 commit comments

Comments
 (0)