{"record":{"id":"7295bfc0819ddb26","repo":"vercel/ai","slug":"transcription-request-was-aborted","errorCode":null,"errorMessage":"Transcription request was aborted","messagePattern":"Transcription request was aborted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/assemblyai/src/assemblyai-transcription-model.ts","lineNumber":286,"sourceCode":"  private async waitForCompletion(\n    transcriptId: string,\n    headers: Record<string, string | undefined> | undefined,\n    abortSignal?: AbortSignal,\n  ): Promise<{\n    transcript: z.infer<typeof assemblyaiTranscriptionResponseSchema>;\n    rawTranscript: unknown;\n    responseHeaders: Record<string, string>;\n  }> {\n    const pollingInterval =\n      this.config.pollingInterval ?? this.POLLING_INTERVAL_MS;\n\n    // Honor a caller-provided fetch (proxy, auth injection, tests) for the\n    // polling GETs, matching the upload/submit calls that use config.fetch.\n    const fetchImpl = this.config.fetch ?? globalThis.fetch;\n\n    while (true) {\n      if (abortSignal?.aborted) {\n        throw new Error('Transcription request was aborted');\n      }\n\n      const response = await fetchImpl(\n        this.config.url({\n          path: `/v2/transcript/${transcriptId}`,\n          modelId: this.modelId,\n        }),\n        {\n          method: 'GET',\n          headers: combineHeaders(\n            this.config.headers?.(),\n            headers,\n          ) as HeadersInit,\n          signal: abortSignal,\n        },\n      );\n\n      if (!response.ok) {","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/assemblyai/src/assemblyai-transcription-model.ts#L268-L304","documentation":"AssemblyAI transcription polling loop checks the caller-supplied abortSignal before each status-polling GET. If the signal is already aborted, it throws this generic Error instead of continuing to poll. It exists so callers can cancel long-running transcription waits (which can take minutes).","triggerScenarios":"Calling transcript()/rawTranscript() on assemblyai.transcriptionModel with an abortSignal that becomes (or already is) aborted while waitForCompletion polls GET /v2/transcript/{id}.","commonSituations":"User cancels a UI request mid-transcription; server request shutdown triggers AbortController; a shared signal aborted by an earlier timeout is reused.","solutions":["Pass a fresh AbortController signal only if you actually want cancellation.","Check signal.aborted before calling and skip transcription if already aborted.","Wrap the call in try/catch and treat this error as an intentional cancel, not a failure.","Increase or remove upstream timeouts that abort the shared signal prematurely."],"exampleFix":"// before\nconst result = await model.transcript({ transcriptId, abortSignal: controller.signal });\n// after\ntry {\n  const result = await model.transcript({ transcriptId, abortSignal: controller.signal });\n} catch (e) {\n  if (controller.signal.aborted) return; // intentional cancellation\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"if (abortSignal?.aborted) {\n  // skip the transcription call entirely\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await model.transcript({ transcriptId, abortSignal });\n} catch (e) {\n  if (abortSignal?.aborted || (e instanceof Error && e.message === 'Transcription request was aborted')) {\n    return; // treat as cancellation\n  }\n  throw e;\n}","preventionTips":["Use a dedicated AbortController per transcription request, not a shared one.","Check signal.aborted before starting long waits.","Surface cancellation to users distinctly from failures."],"tags":["abort","cancellation","transcription","polling"],"backgroundTag":"request-aborted","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}