continuedev/continue · warning

Error killing TTS process:

Error message

Error killing TTS process: 

What it means

This warning is emitted by the TTS (text-to-speech) read function when it tries to kill a previously active speech process before starting a new utterance, and that kill operation fails. It uses core/util/tts.ts's TTS.kill() to terminate the prior say/espeak-style child process. The read call then returns early, so the new message is never spoken.

Source

Thrown at core/util/tts.ts:46

  message = message.trim().replace(/\s+/g, " ");

  return message;
}

export class TTS {
  static os: string | undefined = undefined;
  static handle: ChildProcess | undefined = undefined;
  static messenger: IMessenger<ToCoreProtocol, FromCoreProtocol>;

  static async read(message: string) {
    message = sanitizeMessageForTTS(message);

    try {
      // Kill any active TTS processes
      await TTS.kill();
    } catch (e) {
      console.warn("Error killing TTS process: ", e);
      return;
    }

    switch (TTS.os) {
      case "darwin":
        TTS.handle = exec(`say "${message}"`);
        break;
      case "win32":
        // Replace single quotes on windows
        TTS.handle = exec(
          `powershell -Command "Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${message.replace(
            /'/g,
            "''",
          )}')"`,
        );
        break;
      case "linux":
        TTS.handle = exec(`espeak "${message}"`);

View on GitHub (pinned to 5522c6f44c)

Solutions

  1. Check which TTS command is selected for your OS (TTS.os switch) and verify it is installed (e.g. `say` on macOS, `espeak`/`festival` on Linux)
  2. Avoid issuing read() calls while a previous utterance is active, or debounce/drain TTS requests on the client side
  3. Make TTS.kill() tolerant of already-exited processes (check handle.exitCode !== null before killing) so a stale handle does not abort the new read
  4. If the process handle is unrecoverable, reset TTS.handle to null in the catch so subsequent reads are not permanently blocked

Example fix

// before
try {
  await TTS.kill();
} catch (e) {
  console.warn("Error killing TTS process: ", e);
  return;
}

// after
try {
  await TTS.kill();
} catch (e) {
  console.warn("Error killing TTS process: ", e);
  TTS.handle = null; // recover instead of aborting the new utterance
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await tts.read(message);
} catch (e) {
  // non-fatal: log and continue the chat stream
  console.warn("TTS unavailable:", e);
}

Prevention

When it happens

Trigger: Calling the public read() (driven by llmStreamChat / done handlers) while a previous TTS utterance is still playing, and the underlying child process handle is stale, already exited, or the OS command spawn fails (e.g. say not found on non-darwin, kill signal rejected).

Common situations: Rapid consecutive TTS reads on streamed LLM responses; process handle leaked from a crashed earlier spawn; running on a platform where the configured TTS binary is missing or has wrong permissions.

Related errors


AI-assisted analysis of continuedev/continue@5522c6f44c (2026-08-27). Data as JSON: /api/errors/6cafc472f219a4e0. Report an issue: GitHub.