{"record":{"id":"21610f7401052076","repo":"stablyai/orca","slug":"failed-to-load-dictation-models","errorCode":null,"errorMessage":"Failed to load dictation models","messagePattern":"Failed to load dictation models","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/dictation/mobile-dictation-setup.ts","lineNumber":40,"sourceCode":"  return (\n    message.includes('speech.models.list') &&\n    (error?.code === 'method_not_found' || message.includes('not available to mobile clients'))\n  )\n}\n\nexport function isDictationSetupRequiredError(message: string): boolean {\n  return SETUP_REQUIRED_CODES.has(message) || message.startsWith('voice_model_not_ready:')\n}\n\nexport async function fetchDictationSetup(\n  client: Pick<RpcClient, 'sendRequest'>\n): Promise<MobileSpeechSetup> {\n  const response = await fetchDictationSetupResponse(client)\n  if (!response.ok) {\n    if (isLegacyDesktopSpeechSetupError(response.error)) {\n      throw new Error(LEGACY_DESKTOP_SPEECH_SETUP_MESSAGE)\n    }\n    throw new Error(response.error?.message || 'Failed to load dictation models')\n  }\n  return (response as RpcSuccess).result as MobileSpeechSetup\n}\n\nasync function fetchDictationSetupResponse(client: Pick<RpcClient, 'sendRequest'>) {\n  try {\n    return await client.sendRequest('speech.models.list', null)\n  } catch (error) {\n    if (!(error instanceof LogicalClientCutoverError)) {\n      throw error\n    }\n    // Why: this read can safely repeat on the authenticated replacement; mutation\n    // RPCs must still surface cutover so callers never replay unknown commits.\n    return client.sendRequest('speech.models.list', null)\n  }\n}\n\nexport async function downloadDictationModel(","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/dictation/mobile-dictation-setup.ts#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the desktop host logs for the underlying speech.models.list failure — the message was lost in serialization, the cause is there.","Verify the host connection is authenticated and stable (a cutover mid-call would have been retried; a denial would not).","Retry fetchDictationSetup once after reconnecting; if it persists, escalate to the legacy-desktop upgrade path (error 342) in case the build detection was incomplete."],"exampleFix":"// before\nthrow new Error(response.error?.message || 'Failed to load dictation models')\n\n// after — preserve the code so callers/UI can branch on it\nconst code = response.error?.code\nthrow new Error(response.error?.message || `Failed to load dictation models${code ? ` (${code})` : ''}`)","handlingStrategy":"try-catch","validationCode":"// Verify the connection is healthy before fetching setup\nif (connState !== 'connected') throw new Error('Waiting for desktop...')","typeGuard":"function isDictationSetupLoadError(err: unknown): boolean {\n  return err instanceof Error && err.message === 'Failed to load dictation models'\n}","tryCatchPattern":"try {\n  return await fetchDictationSetup(client)\n} catch (err) {\n  if (isDictationSetupLoadError(err)) {\n    showRetryableErrorToast('Could not load dictation models', { retry: () => fetchDictationSetup(client) })\n    return\n  }\n  throw err\n}","preventionTips":["Inspect the raw response.error.code in development builds to recover the lost cause.","Re-run fetchDictationSetup after a reconnect instead of caching the failure.","Preserve the host error code on the thrown Error so callers can branch without string matching."],"tags":["rpc","dictation","speech","fallback","typescript"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}