{"record":{"id":"8b9dd382a224c049","repo":"pbakaus/impeccable","slug":"impeccable-voice-start-failed","errorCode":null,"errorMessage":"[impeccable.voice] start failed:","messagePattern":"\\[impeccable\\.voice\\] start failed:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"skill/scripts/live-browser.js","lineNumber":10640,"sourceCode":"    };\n\n    rec.onerror = (event) => {\n      const code = event.error || 'unknown';\n      console.warn('[impeccable.voice] recognition error:', code);\n      const message = steerVoiceErrorMessage(code);\n      stopVoice({ suppressSubmit: true, message: message || undefined });\n    };\n\n    rec.onend = () => {\n      if (voiceRecognition !== rec) return;\n      finishVoiceSession();\n    };\n\n    voiceRecognition = rec;\n    try {\n      rec.start();\n    } catch (err) {\n      console.warn('[impeccable.voice] start failed:', err);\n      stopVoice({\n        suppressSubmit: true,\n        message: err?.message?.includes('already started')\n          ? 'Voice input already running'\n          : 'Could not start voice input',\n      });\n    }\n  }\n\n  function steerVoiceContext() {\n    return {\n      mode: 'steer',\n      input: pageChatInput,\n      beforeStart: () => {\n        if (!pageChatExpanded) expandPageChat({ focus: false });\n      },\n      submit: submitSteerMessage,\n    };","sourceCodeStart":10622,"sourceCodeEnd":10658,"githubUrl":"https://github.com/pbakaus/impeccable/blob/2bc2879276c1f321a53c4ca99d3371e411329b52/skill/scripts/live-browser.js#L10622-L10658","documentation":"The live-mode voice input feature calls SpeechRecognition.start() inside a try/catch; when the browser's Web Speech API rejects the synchronous start, it logs this warning, tears the voice UI down via stopVoice(), and shows the user a friendly message ('Voice input already running' if the error mentions 'already started', otherwise 'Could not start voice input'). It is a handled failure path, not a crash — the warning surfaces because a start() attempt was made while the recognition object could not begin listening.","triggerScenarios":"Calling rec.start() when recognition is already active (double-click on the mic button, rapid toggling, Enter keypress racing a click); calling start() on a SpeechRecognition instance that is in an aborted/error state; browsers where the API exists but the recognition service cannot start (e.g. microphone unavailable or blocked); a stale recognition object left over after a previous abort.","commonSituations":"Users double-click or hold the mic toggle so start() fires twice; a page reload or HMR swap leaves an old recognition instance 'already started'; running in browsers with partial Web Speech support (Firefox) or no microphone permission granted; calling start() immediately after stop() without waiting for the onend event.","solutions":["Guard against double-start: track a boolean (e.g. voiceActive) and only call rec.start() when it is false, flipping it in onstart/onend","Before calling start(), call rec.stop() if a previous session may still be running, or create a fresh SpeechRecognition instance per session instead of reusing one","Wait for the onend event of the previous recognition session before invoking start() again","Check microphone permission with navigator.permissions.query({name:'microphone'}) before showing the voice UI, so start() is only called when the mic is available"],"exampleFix":"// before\nvoiceRecognition = rec;\ntry {\n  rec.start();\n} catch (err) { /* handled */ }\n\n// after\nif (voiceActive) return; // ignore duplicate start\nvoiceRecognition = rec;\nrec.onstart = () => { voiceActive = true; };\nrec.onend = () => { voiceActive = false; };\ntry {\n  rec.start();\n} catch (err) {\n  console.warn('[impeccable.voice] start failed:', err);\n}","handlingStrategy":"try-catch","validationCode":"const canStartVoice = () =>\n  typeof (window.SpeechRecognition || window.webkitSpeechRecognition) === 'function' &&\n  !voiceActive;","typeGuard":"function isStartError(err) {\n  return err instanceof Error && /already started/i.test(err.message);\n}","tryCatchPattern":"try {\n  if (!voiceActive) {\n    voiceRecognition = rec;\n    rec.start();\n  }\n} catch (err) {\n  if (/already started/i.test(err?.message ?? '')) {\n    // already running — ignore\n  } else {\n    console.warn('[impeccable.voice] start failed:', err);\n  }\n}","preventionTips":["Maintain a voiceActive flag set in onstart/onend and check it before every start() call","Debounce the mic button/keyboard shortcut so rapid toggles cannot fire start() twice","Create a new SpeechRecognition instance per session instead of reusing a possibly-stale one","Check microphone permission before enabling the voice UI"],"tags":["voice-input","web-speech-api","browser"],"backgroundTag":"invalid-state-transition","analyzedSha":"2bc2879276c1f321a53c4ca99d3371e411329b52","analyzedAt":"2026-09-08T04:51:14.109Z","contentChangedAt":"2026-09-08T04:51:14.109Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}