Skip to content

Fix process abort when espeak-ng rejects the voice (e.g. invalid --kokoro-lang)#3749

Open
ekenberg wants to merge 1 commit into
k2-fsa:masterfrom
ekenberg:fix/espeak-lang-abort
Open

Fix process abort when espeak-ng rejects the voice (e.g. invalid --kokoro-lang)#3749
ekenberg wants to merge 1 commit into
k2-fsa:masterfrom
ekenberg:fix/espeak-lang-abort

Conversation

@ekenberg

@ekenberg ekenberg commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

An unsupported --kokoro-lang value kills the whole process:

$ sherpa-onnx-offline-tts \
    --kokoro-model=./kokoro-multi-lang-v1_0/model.onnx \
    ... \
    --kokoro-lang=en-gb --sid=21 --output-filename=/tmp/t.wav "test"

terminate called after throwing an instance of 'std::runtime_error'
  what():  Failed to set eSpeak-ng voice
Aborted (core dumped)

Easy to hit innocently: the espeak-ng data in kokoro-multi-lang-v1_0 has en-GB-x-rp, en-GB-scotland etc., but no plain en-GB — so asking for British English dumps core, after the model has already been loaded.

Cause

  • piper::phonemize_eSpeak() throws std::runtime_error when espeak_SetVoiceByName() fails (piper-phonemize src/phonemize.cpp)
  • nothing catches it on the way out of Generate()
  • for a user of the C/C++ API, the uncaught exception takes the host process down with it

Fix

Catch it in CallPhonemizeEspeak() — the single espeak boundary shared by the kokoro, matcha and piper frontends. Log the text, the voice and the reason, return no phonemes. The callers already handle empty phoneme lists, so generation fails the same way other invalid parameters do:

Failed to phonemize 'test' with espeak-ng voice 'en-gb': Failed to set eSpeak-ng voice
Error in generating audio. Please read previous error messages.

Exit code 1, no core dump.

Tested

Linux x86_64, kokoro-multi-lang-v1_0:

  • --kokoro-lang=en-gb, --kokoro-lang=xyzzy: SIGABRT before, clean exit 1 after
  • --kokoro-lang=fr, --kokoro-lang=en-GB-x-rp, default (no lang): unchanged, audio verified

Two notes on scope. std::exception is caught rather than only std::runtime_error, since anything escaping through this boundary kills the process the same way. And validating the voice name up front (at Validate() time, before the model loads) would give an even earlier error but needs espeak initialized first — happy to do that as a follow-up if you think it's worth it.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of speech phonemization errors.
    • Prevented unexpected application termination when phonemization fails.
    • Added diagnostic logging with the input text, selected voice, and error details.

piper::phonemize_eSpeak() throws std::runtime_error when
espeak_SetVoiceByName() fails, for example

  sherpa-onnx-offline-tts --kokoro-lang=en-gb ...

(the espeak-ng data shipped with kokoro-multi-lang-v1_0 contains
en-GB-x-rp and other en-GB variants, but no plain en-GB). Nothing
catches the exception on its way out of Generate(), so the process
dies with SIGABRT after the model has already been loaded -- and a
library user of the C/C++ API is taken down with it.

Catch it in CallPhonemizeEspeak(), the single espeak boundary shared
by the kokoro, piper and matcha frontends: log the text, the voice
and the reason, and return no phonemes. The callers already handle
empty phoneme lists, so generation fails with the usual "Error in
generating audio" message and a non-zero exit code, consistent with
how other invalid generation parameters are treated.
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6950eb32-3db6-4c44-abe2-427e6564838e

📥 Commits

Reviewing files that changed from the base of the PR and between 40b75e9 and 1fe2484.

📒 Files selected for processing (1)
  • sherpa-onnx/csrc/piper-phonemize-lexicon.cc

📝 Walkthrough

Walkthrough

CallPhonemizeEspeak now catches exceptions from piper::phonemize_eSpeak, logs the input text, voice, and error message, then clears the phoneme output so callers can detect generation failure.

Changes

Phonemization error handling

Layer / File(s) Summary
Handle phonemization failures
sherpa-onnx/csrc/piper-phonemize-lexicon.cc
Adds <exception> and wraps the mutex-protected phonemization call in exception handling that logs failures and clears the output vector.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preventing process aborts when espeak-ng rejects an invalid voice.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request wraps the call to piper::phonemize_eSpeak in a try-catch block to handle exceptions when an unsupported voice is configured, preventing process termination. The reviewer recommends adding a defensive null check for the phonemes pointer before dereferencing it to avoid potential crashes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +67 to 68
// keep multi threads from calling into piper::phonemize_eSpeak
std::lock_guard<std::mutex> lock(espeak_mutex);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent potential undefined behavior or crashes from dereferencing a null pointer, it is highly recommended to add a defensive null check for the phonemes pointer before locking the mutex and calling piper::phonemize_eSpeak.

  if (!phonemes) {
    SHERPA_ONNX_LOGE("phonemes pointer is null");
    return;
  }

  // keep multi threads from calling into piper::phonemize_eSpeak
  std::lock_guard<std::mutex> lock(espeak_mutex);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All callers pass the address of a local vector, so phonemes cannot be null here — and the surrounding code does not null-check output parameters either. Happy to add it if the maintainer prefers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant