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
- 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)
- Avoid issuing read() calls while a previous utterance is active, or debounce/drain TTS requests on the client side
- Make TTS.kill() tolerant of already-exited processes (check handle.exitCode !== null before killing) so a stale handle does not abort the new read
- 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
- Verify the OS TTS binary (say/espeak) is installed before enabling voice output
- Debounce read() calls so a new utterance is only issued after the previous completes
- Treat TTS failures as non-fatal; never let them break the LLM stream loop
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.