stablyai/orca · error

${RPC error message}

Error message

${RPC error message}

What it means

Thrown inside the speech.dictation.chunk send promise when the host returned {ok:false} for an audio chunk. It is immediately caught by the .catch handler which calls queue.failActiveDictation(dictationId, err) — so the throw is internal control flow that routes the error into the active-dictation failure pipeline rather than bubbling to a caller.

Source

Thrown at mobile/src/hooks/mobile-dictation-audio-chunk.ts:41

  const raw = event.data
  const bytes = raw instanceof Uint8Array ? raw : new Uint8Array(raw)
  const byteLength = bytes.byteLength
  if (!queue.pendingAudioBudget.tryReserve(byteLength)) {
    queue.failActiveDictation(
      dictationId,
      new Error(MOBILE_DICTATION_CONNECTION_SLOW_ERROR_MESSAGE)
    )
    return
  }
  const sendChunk = client
    .sendRequest('speech.dictation.chunk', {
      dictationId,
      audioBase64: bytesToBase64(bytes),
      sampleRate: MOBILE_DICTATION_PCM_SAMPLE_RATE
    })
    .then((response) => {
      if (!response.ok) {
        throw new Error(response.error.message)
      }
    })
    .catch((err) => queue.failActiveDictation(dictationId, err))
    .finally(() => {
      if (queue.shouldReleaseBudget(dictationId)) {
        queue.pendingAudioBudget.release(byteLength)
      }
      queue.pendingChunks.delete(sendChunk)
    })
  queue.pendingChunks.add(sendChunk)
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Because failActiveDictation already cancels the session and surfaces the error to the UI, treat a chunk error as terminal for the current dictationId — do not retry the chunk.
  2. After the failure UI, the user can start a fresh session (new dictationId via createMobileDictationId) which re-runs the full start handshake.
  3. Check the desktop host speech logs for the underlying cause (session not found vs. pipeline error).
  4. If chunk errors recur, verify the host build supports the current speech.dictation.chunk contract.

Example fix

// before
.then((response) => {
  if (!response.ok) {
    throw new Error(response.error.message)
  }
})
.catch((err) => queue.failActiveDictation(dictationId, err))

// after — include the code so the failure UI can branch on session-expired
.then((response) => {
  if (!response.ok) {
    const code = response.error?.code
    const err = new Error(response.error.message)
    if (code) (err as any).code = code
    throw err
  }
})
.catch((err) => queue.failActiveDictation(dictationId, err))
Defensive patterns

Strategy: try-catch

Validate before calling

// Only enqueue chunks when the session is active and host is connected
if (!client || !dictationId || !enabled || !acceptingChunks) return
if (connState !== 'connected') return

Type guard

function isSessionNotFound(err: unknown): boolean {
  const code = (err as any)?.code
  return code === 'session_not_found' || code === 'session_expired'
}

Try / catch

// The existing pipeline already routes this into failActiveDictation;
// callers of failActiveDictation can branch on session-not-found:
const failActiveDictation = (dictationId, err) => {
  if (isSessionNotFound(err)) {
    setError('Dictation session expired. Tap to start a new one.')
    setStatus('idle')
    return
  }
  reportError(err)
}

Prevention

When it happens

Trigger: client.sendRequest('speech.dictation.chunk', {dictationId, audioBase64, sampleRate}) returns {ok:false}. Causes: dictationId unknown to host (session expired/canceled server-side), host transcription pipeline crashed, transport hiccup, host throttling chunks.

Common situations: The desktop dictation session was canceled out from under the mobile client (e.g., user started a new session elsewhere); the host speech process crashed; slow network caused the host to time out the session; chunk payload exceeded a host limit.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/b33bac039cf3730d. Report an issue: GitHub.