{"record":{"id":"f901d7813903e8a1","repo":"different-ai/openwork","slug":"openwork-host-connection-is-not-ready","errorCode":null,"errorMessage":"OpenWork host connection is not ready.","messagePattern":"OpenWork host connection is not ready\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/react-app/domains/session/voice/voice-panel.tsx","lineNumber":483,"sourceCode":"        voiceRealtime.pendingResponse = false;\n        requestRealtimeResponse(channel, false);\n      } else {\n        setRuntimeStatus(voiceRealtime.micMuted ? \"muted\" : \"listening\");\n      }\n      return;\n    }\n    if (type === \"error\") {\n      voiceRealtime.responseInProgress = false;\n      const error = readRecord(event, \"error\");\n      const message = typeof error.message === \"string\" ? error.message : \"Realtime returned an error.\";\n      addEntry(\"system\", message, { error: true });\n      setRuntimeStatus(\"error\", message);\n    }\n  }, [addEntry, props.sessionId, requestRealtimeResponse, setRuntimeStatus]);\n\n  const connectRealtime = useCallback(async (audioInput = true) => {\n    const client = props.client;\n    if (!client) throw new Error(\"OpenWork host connection is not ready.\");\n    if (audioInput && !navigator.mediaDevices?.getUserMedia) throw new Error(\"Microphone capture is unavailable in this runtime.\");\n\n    disconnectRealtime(true);\n    setRuntimeStatus(\"connecting\", \"Minting Realtime session...\");\n    const sessionContext = await loadVoiceSessionContext(props.opencodeBaseUrl, props.openworkToken, props.sessionId);\n    const realtimeSession = await client.createVoiceRealtimeSession({ sessionContext });\n\n    const peer = new RTCPeerConnection();\n    voiceRealtime.peer = peer;\n    if (audioInput) {\n      setRuntimeStatus(\"connecting\", \"Requesting microphone...\");\n      const macPermissionGranted = await requestMacMicrophoneAccess();\n      if (!macPermissionGranted) throw new Error(\"macOS denied microphone access. Enable OpenWork in System Settings > Privacy & Security > Microphone, then restart OpenWork.\");\n      const stream = await navigator.mediaDevices.getUserMedia({\n        audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },\n      });\n      voiceRealtime.stream = stream;\n      setMicDiagnostics(stream);","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/react-app/domains/session/voice/voice-panel.tsx#L465-L501","documentation":"connectRealtime in VoicePanel depends on props.client, the OpenWork host connection object used to mint voice realtime sessions. If it is null/undefined the panel cannot create a realtime session, so it throws before doing any network work.","triggerScenarios":"Calling connectRealtime() when props.client is null — typically because the voice panel rendered before the host client finished connecting, or the host (desktop/opencode backend) connection dropped.","commonSituations":"User clicks the voice connect button while the app is still establishing its host connection; host process crashed or restarted; panel mounted in a state where client was never injected (e.g. headless/web runtime without the bridge).","solutions":["Wait for the host client to be ready (disable the voice button until props.client is set)","Reconnect the app to the OpenWork host / restart the desktop app if the connection dropped","Guard the call: if (!client) show a 'connecting…' or retry state instead of throwing raw","Check host process health/logs if client is persistently null"],"exampleFix":"// before\nconst client = props.client;\nif (!client) throw new Error(\"OpenWork host connection is not ready.\");\n// after\nconst client = props.client;\nif (!client) {\n  setRuntimeStatus(\"error\", \"Host connection not ready — retrying...\");\n  return;\n}","handlingStrategy":"type-guard","validationCode":"if (!props.client) {\n  setRuntimeStatus(\"idle\", \"Host connection not ready.\");\n  return;\n}","typeGuard":"const hasHostClient = (c: typeof props.client): c is NonNullable<typeof props.client> => c != null;","tryCatchPattern":"try {\n  await connectRealtime();\n} catch (e) {\n  if (e instanceof Error && e.message === \"OpenWork host connection is not ready.\") {\n    setRuntimeStatus(\"error\", \"Reconnect to the OpenWork host and try again.\");\n  } else throw e;\n}","preventionTips":["Disable voice controls until the host client is connected","Subscribe to host connection state and auto-retry when it becomes ready","Check client readiness on panel mount","Show a spinner instead of invoking connect during host startup"],"tags":["voice","webrtc","client-not-ready"],"backgroundTag":"client-not-initialized","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}