moeru-ai/airi · error · TypeError

Apple Speech live transcription requires a native stream req

Error message

Apple Speech live transcription requires a native stream request.

What it means

executeAppleSpeechStream only accepts an AppleSpeechStreamRequest-shaped request (detected by isAppleSpeechStreamRequest); passing a generic StreamTranscriptionOptions object without the native request fields throws. The native Electron-side recognizer needs its own request descriptor, not the generic options bag.

Source

Thrown at packages/stage-ui/src/libs/providers/providers/apple-speech/index.ts:197

  }
  finally {
    reader.releaseLock()
  }
}

/**
 * Adapts AIRI's mono PCM16 VAD stream to Apple Speech live transcription.
 *
 * The Provider boundary converts each audio chunk and maps Apple replacement
 * events to AIRI transcript snapshots.
 */
export function executeAppleSpeechStream(options: AppleSpeechStreamOptions): AIRIStreamTranscriptionResult
export function executeAppleSpeechStream(options: StreamTranscriptionOptions): AIRIStreamTranscriptionResult
export function executeAppleSpeechStream(options: StreamTranscriptionOptions): AIRIStreamTranscriptionResult {
  if (!options.inputAudioStream)
    throw new TypeError('Apple Speech live transcription requires an audio stream.')
  if (!isAppleSpeechStreamRequest(options))
    throw new TypeError('Apple Speech live transcription requires a native stream request.')

  const inputSampleRate = options.inputSampleRate ?? 16000
  const live = streamAppleSpeechTranscription({
    ...options,
    inputSampleRate,
  })
  const inputPump = pumpPcm16Input(options.inputAudioStream, live.input.getWriter())
  void inputPump.catch(() => {})

  return {
    fullStream: live.fullStream.pipeThrough(new TransformStream({
      transform(event, controller) {
        controller.enqueue(appleEventSnapshot(event))
      },
    })),
    text: Promise.all([live.text, inputPump]).then(([text]) => text),
    textStream: live.partialStream,
  }

View on GitHub (pinned to f679616c34)

Solutions

  1. Construct an AppleSpeechStreamRequest (the native request object expected by the Apple Speech provider) and pass it in options.
  2. In generic dispatch code, branch per provider: build Apple-specific options for Apple Speech instead of reusing the generic bag.
  3. Inspect isAppleSpeechStreamRequest / the AppleSpeechStreamOptions type to see exactly which fields mark a valid native request.

Example fix

// before
const result = executeAppleSpeechStream({ inputAudioStream: stream })

// after
const result = executeAppleSpeechStream({
  inputAudioStream: stream,
  ...createAppleSpeechStreamRequest({ locale: 'en-US' }),
})
Defensive patterns

Strategy: type-guard

Validate before calling

if (!isAppleSpeechStreamRequest(options)) {
  options = buildAppleSpeechStreamOptions(options) // construct the native request shape
}
const result = executeAppleSpeechStream(options)

Type guard

// reuse the library's own predicate when exported, otherwise mirror it:
function isAppleSpeechStreamOptions(options: unknown): options is AppleSpeechStreamOptions {
  return isAppleSpeechStreamRequest(options as StreamTranscriptionOptions)
}

Try / catch

null

Prevention

When it happens

Trigger: Calling executeAppleSpeechStream with a generic StreamTranscriptionOptions that has inputAudioStream but lacks the Apple Speech native request fields, so isAppleSpeechStreamRequest(options) returns false.

Common situations: Provider-agnostic dispatch code forwarding one shared options object to all providers; a caller satisfying the stream check but never building the Apple-specific request; refactors that changed the AppleSpeechStreamOptions shape and dropped the request field.

Related errors


AI-assisted analysis of moeru-ai/airi@f679616c34 (2026-08-28). Data as JSON: /api/errors/58eac2177d699a62. Report an issue: GitHub.