How I Fix a Callback Error When Creating My Speech Recognition Game

I’ve always wanted to build an interactive voice controlled game something fun, spooky, and immersive. So, I started experimenting with pyttsx3, speech_recognition, and some simple logic to bring my idea to life. Everything was going great until I hit a frustrating and confusing error just when my player chose the “knife” in the game.

Here’s how I diagnosed it, fixed it, and ended up with a more reliable and feature rich version of the game.

The Mysterious Callback Error

As soon as I reached the part where the player picks the “knife,” I got this error:

Exception ignored on calling ctypes callback function: <function catch_errors.<locals>.call_with_this at ...>
TypeError: 'NoneType' object is not callable

What made it even more confusing was that the game didn’t crash, but it just stopped responding properly. It took me a bit of debugging to trace the problem.

What Was Really Causing It

The message was pointing me toward ctypes, comtypes, and logging, but the root cause was much simpler:

time.sleep(a)

I had used a variable a for the pause duration without defining it. Since this line was buried in a speech callback function (pyttsx3 uses them internally), the actual exception was suppressed, leading to a cryptic traceback and silent failure.

Lesson learned: Always define your variables before using them and never assume a callback will surface exceptions the way normal code does.

How I Fix the Problem

Define a clear variable

I replaced a with a proper pause_time = 2, so the pause duration is clearly managed and can be reused.

Update command logic

I noticed I wasn’t capturing new voice input properly during nested choices (e.g when asking for the weapon or the next action). I fixed that by creating a function to handle all voice input and calling it every time I needed a command.

Add better error handling

I added try-except blocks for speech_recognition to handle things like microphone issues or misunderstood speech.

Final Working Version of the Game

import time
import pyttsx3
import speech_recognition as sr

# Initialize TTS engine
engine = pyttsx3.init()
voices = engine.getProperty("voices")
engine.setProperty("voice", voices[1].id)
engine.setProperty("rate", 150)

# Define pause duration
pause_time = 2

# Function to speak text
def speak(text):
engine.say(text)
engine.runAndWait()

# Function to listen and recognize speech
def get_voice_command(prompt=""):
if prompt:
speak(prompt)
listener = sr.Recognizer()
with sr.Microphone() as source:
print("Listening...")
listener.adjust_for_ambient_noise(source)
voice = listener.listen(source)
try:
command = listener.recognize_google(voice)
return command.lower()
except sr.UnknownValueError:
speak("Sorry, I didn't catch that.")
return None
except sr.RequestError:
speak("Speech service is unavailable.")
return None

# Game logic
def play_game():
name = "Player" # Placeholder name
speak("As you head down the dark alleyway, the light you saw quickly gets further and further away. You then notice two doors.")
time.sleep(pause_time)
command = get_voice_command("Say Left or Say Right")
if not command:
return

if "left" in command:
speak("You think just because this path isn't as dark it is safer. But you may be in for a surprise.")
time.sleep(pause_time)
speak("The door opens and you are faced with the brightest light you have ever seen.")
time.sleep(3)
speak("GAME OVER")
elif "right" in command:
speak("You are headed down a dark path but the reward may be great!")
time.sleep(pause_time)
weapon = get_voice_command("Say Knife or say Bow and Arrow to choose your weapon")
if not weapon:
return
if "knife" in weapon:
speak("You pick up the knife and notice it already has dried blood on it. Whose blood is this, you wonder.")
time.sleep(pause_time)
speak(f"You head further into the dark room and you hear very faintly your name being called. {name}... {name}... Please come help me.")
time.sleep(pause_time)
choice = get_voice_command("Say voice to head towards the voice, or say room to keep exploring the room")
if choice:
if "voice" in choice:
speak("You follow the voice. It's getting louder... but suddenly the door slams shut behind you.")
speak("GAME OVER.")
elif "room" in choice:
speak("You keep exploring and discover a hidden staircase. The adventure continues...")
else:
speak("Invalid choice.")
elif "bow" in weapon or "arrow" in weapon:
speak("You take the bow and arrow. A classic choice for the careful adventurer.")
speak("You hear a noise from above... something is watching.")
else:
speak("You didn't choose a valid weapon.")
else:
speak("You didn't say left or right clearly.")

# Run the game
play_game()

What I Want to Add Next

Now that the base works, I have tons of ideas to expand the game:

  • Use gTTS for smoother, pre-generated voices if I want better narration.
  • Add sound effects using playsound() to make things feel more intense.
  • Track player choices to build branching story paths and outcomes.
  • Handle “Repeat” or “Help” commands for accessibility.
  • Use a GUI with tkinter or pygame to give players visual feedback and atmosphere.

Final Thought

Debugging this callback error taught me a lot not just about error handling, but also about how voice interfaces behave under the hood. It’s a totally different world from traditional input-based games, but with a little care and structure, it can be incredibly rewarding.

Related blog posts