{"record":{"id":"767b50555416da84","repo":"stablyai/orca","slug":"rpc-error-message-767b50","errorCode":null,"errorMessage":"${RPC error message}","messagePattern":"\\$\\{RPC error message\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mobile/src/hooks/mobile-dictation-desktop-start.ts","lineNumber":70,"sourceCode":"  setIdleIfGenerationCurrent(options)\n  const cleanups: Promise<unknown>[] = [\n    client.sendRequest('speech.dictation.cancel', { dictationId })\n  ]\n  if (releaseKeepAwake) {\n    cleanups.push(keepAwakeOwner.release(dictationId))\n  }\n  await Promise.allSettled(cleanups)\n}\n\nexport async function startMobileDictationDesktopSession(\n  options: StartMobileDictationDesktopSessionOptions\n): Promise<boolean> {\n  const { client, dictationId, keepAwakeOwner } = options\n\n  try {\n    const response = await client.sendRequest('speech.dictation.start', { dictationId })\n    if (!response.ok) {\n      throw new Error(response.error.message)\n    }\n  } catch (err) {\n    const wasCurrent = isCurrentStart(options)\n    options.clearActiveId(dictationId)\n    await client.sendRequest('speech.dictation.cancel', { dictationId }).catch(() => undefined)\n    // Awaited cleanup may overlap a newer start; stale work must not reset or\n    // report over the replacement session.\n    const shouldReport = wasCurrent && canReportStartFailure(options)\n    setIdleIfGenerationCurrent(options)\n    if (!shouldReport) {\n      return false\n    }\n    throw err\n  }\n\n  if (!isCurrentStart(options)) {\n    await cancelStaleStart(options, { releaseKeepAwake: false })\n    return false","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/hooks/mobile-dictation-desktop-start.ts#L52-L88","documentation":"Thrown by startMobileDictationDesktopSession when speech.dictation.start returned {ok:false}. It is thrown inside a try block whose catch runs cleanup (cancel RPC, clearActiveId, idle status) and only re-throws to the caller if the session is still current and enabled. So this error represents the host refusing to start the remote dictation session.","triggerScenarios":"client.sendRequest('speech.dictation.start', {dictationId}) returns {ok:false}. Causes: desktop speech subsystem not ready, model not loaded, another dictation session already active, host lacks mic permission, host build does not support the start contract.","commonSituations":"User taps mic before the model finished loading on desktop; the desktop's speech runtime crashed; another mobile device started dictation on the same host; the desktop OS denied microphone access to Orca.","solutions":["Ensure fetchDictationSetup reports isDictationReady(setup) === true (enabled + selectedModelId with status 'ready') before invoking start.","On a 'session_already_active' style code, call speech.dictation.cancel for any stale dictationId, then retry start once.","Check the desktop's microphone permission and speech runtime status in the host logs.","If the host build is too old, route the user to the desktop upgrade flow."],"exampleFix":"// before\nconst response = await client.sendRequest('speech.dictation.start', { dictationId })\nif (!response.ok) {\n  throw new Error(response.error.message)\n}\n\n// after — attach the code so the catch can branch\nconst response = await client.sendRequest('speech.dictation.start', { dictationId })\nif (!response.ok) {\n  const err = new Error(response.error.message) as Error & { code?: string }\n  if (response.error?.code) err.code = response.error.code\n  throw err\n}","handlingStrategy":"try-catch","validationCode":"// Confirm dictation is ready before starting\nconst setup = await fetchDictationSetup(client)\nif (!isDictationReady(setup)) throw new Error('Dictation is not ready')","typeGuard":"function isSessionAlreadyActive(err: unknown): boolean {\n  return (err as any)?.code === 'session_already_active'\n}","tryCatchPattern":"try {\n  return await startMobileDictationDesktopSession(options)\n} catch (err) {\n  if (isSessionAlreadyActive(err)) {\n    await client.sendRequest('speech.dictation.cancel', { dictationId }).catch(() => undefined)\n    return await startMobileDictationDesktopSession(options)\n  }\n  throw err\n}","preventionTips":["Gate the mic button on isDictationReady(setup) — do not call start if no ready model is selected.","Cancel any stale session before starting a new one.","Attach the host error code to the thrown Error so the catch can branch on session_already_active vs. not_ready."],"tags":["rpc","dictation","speech","session-start","typescript"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}