2025, Sep 23 17:00

Fixing pyttsx3 runAndWait when only the first phrase plays: reliable patterns and code

Troubleshooting pyttsx3 when runAndWait speaks only the first phrase. Learn why the second utterance is skipped and fix it by re-initializing or queueing.

When you use pyttsx3 to speak more than one phrase in a row, calling runAndWait repeatedly can lead to only the first utterance being spoken. This shows up in simple scenarios like a spelling game where you read a word, wait for the user, then read the next one. The second phrase never plays, even though the code keeps executing.

Minimal reproduction

The following snippet requests two utterances back to back with separate runAndWait calls, and stops the engine after each run. Only the first phrase is spoken.

import pyttsx3

vocal = pyttsx3.init()
vocal.say("Hello World")
vocal.runAndWait()
vocal.stop()

vocal.say("Hi")
vocal.runAndWait()
vocal.stop()

What’s actually happening

The control flow continues normally; the issue is not that the program hangs. Adding prints confirms both blocks execute:

import pyttsx3

speaker = pyttsx3.init()
speaker.say("Hello World")
speaker.runAndWait()
print("HELLO CALLED")
speaker.stop()

speaker.say("Hi")
speaker.runAndWait()
print("HI CALLED")
speaker.stop()

Both markers are printed, which points at the engine state rather than your Python control flow. The observed behavior suggests the text isn’t being queued or processed on the second pass as expected. There are reports that stopping clears the event queue rather than performing a full teardown, and there is also an upstream report titled “Only the first two messages in the queue are played.” Behavior can vary by environment: for instance, some setups report normal playback, others don’t. In the referenced Windows 11 setup with the default engine, the second phrase doesn’t play.

Practical fixes

There are two reliable ways to move forward without changing your overall program logic.

Approach 1: Re-initialize the engine per utterance

Create a fresh engine each time you need to speak, then run and release it. This isolates state across calls and avoids the problematic reuse.

import pyttsx3

def speak_once(msg):
    tts = pyttsx3.init()
    tts.say(msg)
    tts.runAndWait()
    del tts

speak_once("Hello World")
print("SAYING HELLO WORLD")

speak_once("Hi")
print("SAYING HI")

If you prefer to defer runAndWait and still reset the engine before each enqueue, return the new instance and drive it outside:

import pyttsx3

voice_ref = ""

def queue_with_reset(text):
    global voice_ref
    del voice_ref
    voice_ref = pyttsx3.init()
    voice_ref.say(text)
    return voice_ref

runner = queue_with_reset("Hello World")
runner.runAndWait()
print("SAYING HELLO WORLD")

runner = queue_with_reset("Hi")
runner.runAndWait()
print("SAYING HI")

If you sometimes want multiple phrases in one batch, reset only when needed and reuse the same instance to accumulate items:

import pyttsx3

engine_ref = None

def enqueue(text, reset=True):
    global engine_ref
    if reset:
        engine_ref = pyttsx3.init()
        engine_ref.say(text)
        return engine_ref
    engine_ref.say(text)
    return engine_ref

runner = enqueue("Hello World")
runner.runAndWait()
print("SAYING HELLO WORLD")

runner = enqueue("Hi")
runner.runAndWait()
print("SAYING HI")

runner = enqueue("Hi")
runner = enqueue("Hello", reset=False)
runner.runAndWait()

Approach 2: Queue all phrases first, then run once

Skip the intermediate runAndWait. Enqueue everything that should be spoken, run once, and stop afterward.

import pyttsx3

narrator = pyttsx3.init()
narrator.say("Hello World")
narrator.say("Hi")
narrator.runAndWait()
narrator.stop()

Why this matters

In interactive applications like spelling or listening exercises, you often need predictable sequencing: speak, wait for input, speak again. If the engine mishandles the second enqueue after a stop or across runs, the UX breaks. Understanding how to isolate engine state or batch utterances gives you deterministic behavior across sessions and helps avoid silent failures that are hard to debug.

Takeaways

When runAndWait is required multiple times, reusing the same engine instance can fail to play subsequent phrases on some setups. You can either re-initialize the engine for each utterance or queue phrases and run once. If you rely on repeated prompts, prefer the per-call initialization pattern; if you’re producing continuous speech, enqueue first and run once. If your environment behaves differently, note that outcomes vary by OS and library version, and there are known reports about queue handling. Test on your target platform and keep the engine lifecycle simple to avoid surprises.

The article is based on a question from StackOverflow by atthegreatestworld and an answer by Aadvik.