{"record":{"id":"dc88b326a075bdba","repo":"tinyhumansai/openhuman","slug":"ptt-audio-recorder-stop-threw","errorCode":null,"errorMessage":"[ptt-audio] recorder.stop() threw","messagePattern":"\\[ptt-audio\\] recorder\\.stop\\(\\) threw","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/features/voice/pttAudio.ts","lineNumber":90,"sourceCode":"\nexport async function finalizePttAudio(): Promise<FinalizedAudio> {\n  if (!active) {\n    throw new Error('[ptt-audio] finalize called with no active recorder');\n  }\n  const session = active;\n  active = null;\n\n  const done = new Promise<void>(resolve => {\n    if (session.recorder.state === 'inactive') {\n      resolve();\n      return;\n    }\n    session.recorder.addEventListener('stop', () => resolve(), { once: true });\n  });\n  try {\n    if (session.recorder.state !== 'inactive') session.recorder.stop();\n  } catch (err) {\n    console.warn('[ptt-audio] recorder.stop() threw', err);\n  }\n  await done;\n  stopTracks(session.stream);\n\n  const blob = new Blob(session.chunks, { type: session.recorder.mimeType || 'audio/webm' });\n  const buffer = await blob.arrayBuffer();\n  const durationMs = Math.round(window.performance.now() - session.startedAt);\n  console.debug('[ptt-audio] finalized', { durationMs, bytes: buffer.byteLength });\n  return { buffer, durationMs };\n}\n\nexport async function cancelPttAudio(): Promise<void> {\n  if (!active) return;\n  const session = active;\n  active = null;\n  try {\n    if (session.recorder.state !== 'inactive') session.recorder.stop();\n  } catch {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/features/voice/pttAudio.ts#L72-L108","documentation":"Recoverable guard in finalizePttAudio(): calling .stop() on a MediaRecorder whose state changed between the check and the call (or that the browser invalidated) throws synchronously. The catch warns and the flow continues waiting for the already-registered 'stop' event promise, so finalization still settles — the message records a hiccup, not a lost recording.","triggerScenarios":"Thrown at app/src/features/voice/pttAudio.ts:90 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}