{"record":{"id":"568da2339001521f","repo":"moeru-ai/airi","slug":"audio-stream-or-file-is-required-for-streaming-tra","errorCode":null,"errorMessage":"Audio stream or file is required for streaming transcription.","messagePattern":"Audio stream or file is required for streaming transcription\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/libs/providers/stream-transcription/index.ts","lineNumber":48,"sourceCode":"  inputAudioStream?: ReadableStream<AudioChunk>\n  inputStream?: ReadableStream<AudioChunk>\n}\n\nfunction createDeferred<T>() {\n  let resolve!: (value: T | PromiseLike<T>) => void\n  let reject!: (reason?: unknown) => void\n  const promise = new Promise<T>((res, rej) => {\n    resolve = res\n    reject = rej\n  })\n\n  return { promise, resolve, reject }\n}\n\nfunction resolveAudioStream(options: StreamTranscriptionOptions): ReadableStream<AudioChunk> {\n  const stream = options.inputAudioStream ?? options.inputStream ?? options.file?.stream()\n  if (!stream)\n    throw new TypeError('Audio stream or file is required for streaming transcription.')\n\n  return stream as ReadableStream<AudioChunk>\n}\n\nfunction parseSSELine(line: string): AIRIStreamTranscriptionDelta | undefined {\n  if (!line || !line.startsWith('data:'))\n    return undefined\n\n  const content = line.slice('data:'.length)\n  const data = content.startsWith(' ') ? content.slice(1) : content\n  if (!data)\n    return undefined\n\n  return JSON.parse(data) as AIRIStreamTranscriptionDelta\n}\n\nfunction createSSETransformer() {\n  const decoder = new TextDecoder()","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/libs/providers/stream-transcription/index.ts#L30-L66","documentation":"A synchronous TypeError thrown by resolveAudioStream when none of options.inputAudioStream, options.inputStream, or options.file is provided to streamTranscription(). The adapter needs exactly one audio source to POST to the transcription endpoint; with no source it cannot form a request body. It fails fast before any network call.","triggerScenarios":"Calling streamTranscription({}) or streamTranscription({ baseURL, headers }) with no audio source; passing options.file as a value whose .stream() is undefined; a code path that constructs options dynamically and forgets to populate one of the three fields.","commonSituations":"Refactor that renamed inputAudioStream/inputStream and forgot to update a caller; a Hearing module integration where the mic stream is conditionally created and the falsy branch still calls streamTranscription; tests that call the adapter without a fixture file.","solutions":["Ensure exactly one of inputAudioStream, inputStream, or file is set before calling streamTranscription; pick the most specific source for your caller.","If using a Blob/File, pass it via options.file and confirm it is non-null.","Add a caller-side guard that skips transcription when no audio source is available rather than relying on the throw.","In tests, provide a Blob fixture (e.g. new Blob([bytes], { type: 'audio/wav' })) as options.file."],"exampleFix":"// before\nconst result = streamTranscription({ baseURL, headers })\n\n// after: pass an explicit source and guard at the call site\nif (!micStream && !audioFile)\n  return\nconst result = streamTranscription({\n  baseURL,\n  headers,\n  inputAudioStream: micStream ?? undefined,\n  file: audioFile ?? undefined,\n})","handlingStrategy":"validation","validationCode":"import type { StreamTranscriptionOptions } from '../stream-transcription'\n\nfunction hasAudioSource(options: StreamTranscriptionOptions): boolean {\n  return !!(options.inputAudioStream ?? options.inputStream ?? options.file?.stream())\n}\n\n// before calling\nif (!hasAudioSource(options)) {\n  // skip transcription or throw a caller-side error with more context\n  return\n}\nconst result = streamTranscription(options)","typeGuard":"function isReadableStreamLike(value: unknown): value is ReadableStream {\n  return typeof value === 'object' && value !== null\n    && typeof (value as ReadableStream).getReader === 'function'\n}\n\nfunction resolveAudioSource(options: StreamTranscriptionOptions): ReadableStream | undefined {\n  if (options.inputAudioStream && isReadableStreamLike(options.inputAudioStream))\n    return options.inputAudioStream\n  if (options.inputStream && isReadableStreamLike(options.inputStream))\n    return options.inputStream\n  if (options.file && typeof options.file.stream === 'function')\n    return options.file.stream()\n  return undefined\n}","tryCatchPattern":null,"preventionTips":["Always pass exactly one of inputAudioStream, inputStream, or file; document which takes precedence in the caller.","Guard at the call site so the adapter never runs without a source.","In tests, always provide a Blob fixture as file to avoid this synchronous throw."],"tags":["validation","typescript","transcription","streaming","programmer-error"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}