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

  1. Guard against double-start: track a boolean (e.g. voiceActive) and only call rec.start() when it is false, flipping it in onstart/onend
  2. 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
  3. Wait for the onend event of the previous recognition session before invoking start() again
  4. 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

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


AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08). Data as JSON: /api/errors/8b9dd382a224c049. Report an issue: GitHub.