stablyai/orca · error · Error

Failed to load dictation models

Error message

Failed to load dictation models

What it means

Fallback error from fetchDictationSetup when speech.models.list fails, the error is NOT a legacy-desktop setup error, and the host supplied no error.message. It is the generic tail of the error-mapping chain — the real cause lives on the host side and was not serialized with a usable message.

Source

Thrown at mobile/src/dictation/mobile-dictation-setup.ts:40

  return (
    message.includes('speech.models.list') &&
    (error?.code === 'method_not_found' || message.includes('not available to mobile clients'))
  )
}

export function isDictationSetupRequiredError(message: string): boolean {
  return SETUP_REQUIRED_CODES.has(message) || message.startsWith('voice_model_not_ready:')
}

export async function fetchDictationSetup(
  client: Pick<RpcClient, 'sendRequest'>
): Promise<MobileSpeechSetup> {
  const response = await fetchDictationSetupResponse(client)
  if (!response.ok) {
    if (isLegacyDesktopSpeechSetupError(response.error)) {
      throw new Error(LEGACY_DESKTOP_SPEECH_SETUP_MESSAGE)
    }
    throw new Error(response.error?.message || 'Failed to load dictation models')
  }
  return (response as RpcSuccess).result as MobileSpeechSetup
}

async function fetchDictationSetupResponse(client: Pick<RpcClient, 'sendRequest'>) {
  try {
    return await client.sendRequest('speech.models.list', null)
  } catch (error) {
    if (!(error instanceof LogicalClientCutoverError)) {
      throw error
    }
    // Why: this read can safely repeat on the authenticated replacement; mutation
    // RPCs must still surface cutover so callers never replay unknown commits.
    return client.sendRequest('speech.models.list', null)
  }
}

export async function downloadDictationModel(

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Check the desktop host logs for the underlying speech.models.list failure — the message was lost in serialization, the cause is there.
  2. Verify the host connection is authenticated and stable (a cutover mid-call would have been retried; a denial would not).
  3. Retry fetchDictationSetup once after reconnecting; if it persists, escalate to the legacy-desktop upgrade path (error 342) in case the build detection was incomplete.

Example fix

// before
throw new Error(response.error?.message || 'Failed to load dictation models')

// after — preserve the code so callers/UI can branch on it
const code = response.error?.code
throw new Error(response.error?.message || `Failed to load dictation models${code ? ` (${code})` : ''}`)
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the connection is healthy before fetching setup
if (connState !== 'connected') throw new Error('Waiting for desktop...')

Type guard

function isDictationSetupLoadError(err: unknown): boolean {
  return err instanceof Error && err.message === 'Failed to load dictation models'
}

Try / catch

try {
  return await fetchDictationSetup(client)
} catch (err) {
  if (isDictationSetupLoadError(err)) {
    showRetryableErrorToast('Could not load dictation models', { retry: () => fetchDictationSetup(client) })
    return
  }
  throw err
}

Prevention

When it happens

Trigger: client.sendRequest('speech.models.list', null) returns {ok:false} with response.error falsy or response.error.message empty, and isLegacyDesktopSpeechSetupError returned false. Also reached when fetchDictationSetupResponse throws a non-cutover error that is re-thrown.

Common situations: The desktop speech subsystem failed to enumerate models (model directory missing, corrupt config); the transport returned a malformed error object; an authentication denial on the host surfaced as a code-only error.

Related errors


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