AI Portfolio Lab Tools Games Blog Feedback
All Posts

Final Means Twice

I implemented voice for my system. Think Jarvis. Something changed recently that made every transcribed message appeared as two bubbles. The event was called final:true. Pipecat emits it twice per utterance.

1 min read
voicepipecatdebuggingmission-control

When an event is named final, you build like it fires once.

I’ve been continually trying to improve the voice capability in my Mission Control’s Board Room, where I talk to my board of agents. The pipeline goes through Pipecat, a voice-to-voice framework. Speech comes in, the Board Room renders a user message bubble.

It worked. Something changed along the way where every time I said something, two identical bubbles appeared, back to back. Like the system couldn’t decide which version of what I’d said was real. The Board Room started looking like a group chat where one person double-sends every single message.

I went looking in Pipecat’s event model. The signal for “transcription complete” is called final:true. Not “estimate” or “provisional.” Final. Except Pipecat emits it twice per utterance. If you listen for final:true, you get it twice. If you render a bubble on each reception, you get two.

The fix was a ref, lastUserFinalRef. Track the last final transcript and when it arrived. Same text within 1.5 seconds? Drop it.

The fix landed on the second build attempt. The first went stale when master moved mid-repair, an auto-sync commit landing at exactly the wrong moment. So the fix for a double-send bug had to recover from an infrastructure race before it could ship. Some days the glitch runs all the way down.

I’m not calling this out as a Pipecat problem. It does what it does well. But when you’re building at the seam between systems you didn’t write, event names are documentation, not contracts. final:true is done in your mental model. In the framework’s, it turns out to mean done, and then done again.

Past me should have read the event model before auditing his own render logic.