jamiepine/voicebox · error · RuntimeError
TTS output remained unstable after retrying smaller text chu
Error message
TTS output remained unstable after retrying smaller text chunks
What it means
Raised by generate_one() in backend/utils/chunked_tts.py when the runaway detector still flags a chunk's audio as unstable after the retry budget is exhausted — specifically when retry_depth >= MAX_RUNAWAY_RETRIES (2) or the chunk is already at/below MIN_RUNAWAY_RETRY_CHARS (100). It means the TTS backend keeps producing speech-then-silence-then-hallucination shapes even on small inputs, so further splitting is futile.
Source
Thrown at backend/utils/chunked_tts.py:268
-------
(audio, sample_rate) : Tuple[np.ndarray, int]
"""
async def generate_one(
chunk_text: str,
chunk_seed: int | None,
retry_depth: int = 0,
) -> tuple[np.ndarray, int]:
chunk_audio, chunk_sr = await backend.generate(
chunk_text,
voice_prompt,
language,
chunk_seed,
instruct,
)
if runaway_detector is not None and runaway_detector(chunk_audio, chunk_sr):
if retry_depth >= MAX_RUNAWAY_RETRIES or len(chunk_text) <= MIN_RUNAWAY_RETRY_CHARS:
raise RuntimeError(
"TTS output remained unstable after retrying smaller text chunks"
)
retry_max_chars = max(MIN_RUNAWAY_RETRY_CHARS, len(chunk_text) // 2)
retry_chunks = split_text_into_chunks(chunk_text, retry_max_chars)
if len(retry_chunks) <= 1:
raise RuntimeError("Unable to split unstable TTS output for retry")
logger.warning(
"Detected unstable TTS output for %d chars; retrying as %d smaller chunks",
len(chunk_text),
len(retry_chunks),
)
retry_audio: list[np.ndarray] = []
for i, retry_text in enumerate(retry_chunks):
retry_seed = (
chunk_seed + ((retry_depth + 1) * 1000) + i
if chunk_seed is not NoneView on GitHub (pinned to 51f49dea19)
Solutions
- Shorten or rephrase the input text around the failing region and retry.
- Change the seed (per-request seed) so a different decoding path is taken.
- Inspect the voice prompt / reference audio quality; re-clone from cleaner samples if the cloned voice destabilizes generation.
- If recurring, report the failing text + engine + model version as a model bug; consider disabling the runaway_detector only if you accept unbounded output risk.
- Increase MAX_RUNAWAY_RETRIES in chunked_tts.py only as a last resort (it raises latency, not quality).
Example fix
// before: long input always trips runaway after retries
audio, sr = await generate_chunked_tts(backend, long_text, voice_prompt, ...)
// after: split the input externally and use a different seed
for part in manually_split(long_text):
audio, sr = await generate_chunked_tts(backend, part, voice_prompt, seed=new_seed, ...) Defensive patterns
Strategy: fallback
Validate before calling
from backend.utils.chunked_tts import MIN_RUNAWAY_RETRY_CHARS, DEFAULT_MAX_CHUNK_CHARS
import re
def chunk_is_safe_to_retry(text: str) -> bool:
# avoid feeding chunks that are already at the retry floor or unsplittable
if len(text) <= MIN_RUNAWAY_RETRY_CHARS:
return False
if len(text) > DEFAULT_MAX_CHUNK_CHARS:
return False
# must contain at least one breakable boundary so a retry split yields >1 piece
return len(re.findall(r'\S{1,120}', text)) > 1 or ' ' in text Try / catch
try:
audio, sr = await generate_chunked_tts(backend, text, voice_prompt, ...)
except RuntimeError as e:
if 'remained unstable' in str(e):
# fall back to a different seed or shorter pre-split text
audio, sr = await generate_chunked_tts(backend, shorten(text), voice_prompt, seed=alt_seed, ...)
else:
raise Prevention
- Keep input chunks reasonably sized and avoid pathological long sentences.
- Vary the seed per request so a deterministic bad decoding path is not always hit.
- Use clean reference audio for cloned voices; bad prompts destabilize generation.
- Do not disable the runaway detector — it prevents unbounded output cost.
When it happens
Trigger: A pathological input chunk that triggers model EOS-miss/hallucination on every generation; a voice prompt that destabilizes the model; an engine bug producing runaway output; very long single sentences that cannot be split below 100 chars; deterministic seed always yielding runaway for the given text.
Common situations: Chatterbox/tada engines hallucinating after long silences; certain emoji/special-character inputs confusing the model; a cloned voice prompt with bad reference audio; model checkpoint regression; chunk that is one giant unbreakable token run.
Related errors
- Unable to split unstable TTS output for retry
- HTTP error! status: ${response.status}
- HTTP ${res.status}
- Navigator API is not available. This might be a Tauri config
- Microphone access is not available. Please ensure: 1. The ap
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/579f71527902b405.
Report an issue: GitHub.