pbakaus/impeccable · warning
[impeccable.voice] start failed:
Error message
[impeccable.voice] start failed:
What it means
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.
Source
Thrown at skill/scripts/live-browser.js:10640
};
rec.onerror = (event) => {
const code = event.error || 'unknown';
console.warn('[impeccable.voice] recognition error:', code);
const message = steerVoiceErrorMessage(code);
stopVoice({ suppressSubmit: true, message: message || undefined });
};
rec.onend = () => {
if (voiceRecognition !== rec) return;
finishVoiceSession();
};
voiceRecognition = rec;
try {
rec.start();
} catch (err) {
console.warn('[impeccable.voice] start failed:', err);
stopVoice({
suppressSubmit: true,
message: err?.message?.includes('already started')
? 'Voice input already running'
: 'Could not start voice input',
});
}
}
function steerVoiceContext() {
return {
mode: 'steer',
input: pageChatInput,
beforeStart: () => {
if (!pageChatExpanded) expandPageChat({ focus: false });
},
submit: submitSteerMessage,
};View on GitHub (pinned to 2bc2879276)
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
Example fix
// before
voiceRecognition = rec;
try {
rec.start();
} catch (err) { /* handled */ }
// after
if (voiceActive) return; // ignore duplicate start
voiceRecognition = rec;
rec.onstart = () => { voiceActive = true; };
rec.onend = () => { voiceActive = false; };
try {
rec.start();
} catch (err) {
console.warn('[impeccable.voice] start failed:', err);
} Defensive patterns
Strategy: try-catch
Validate before calling
const canStartVoice = () => typeof (window.SpeechRecognition || window.webkitSpeechRecognition) === 'function' && !voiceActive;
Type guard
function isStartError(err) {
return err instanceof Error && /already started/i.test(err.message);
} Try / catch
try {
if (!voiceActive) {
voiceRecognition = rec;
rec.start();
}
} catch (err) {
if (/already started/i.test(err?.message ?? '')) {
// already running — ignore
} else {
console.warn('[impeccable.voice] start failed:', err);
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Live browser script part missing: ${part.name} (${part.path}
- ${cap.error}
- [impeccable] scan failed
- Error: {}
- `WebAssembly.instantiateStreaming` failed because your serve
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/8b9dd382a224c049.
Report an issue: GitHub.