Mintplex-Labs/anything-llm · warning · Error
No text to predict on.
Error message
No text to predict on.
What it means
Thrown inside the Piper TTS Web Worker: runStream splits the submitted text into synthesis chunks, and when splitIntoChunks returns an empty list the stream cannot start, so this Error is raised. It is pure input validation — PIPER_SESSION.predict is never called and no audio work happens. The error propagates through the worker's error handling rather than any HTTP layer.
Solutions
- Guard at the call site: skip TTS entirely when !text.trim().
- Inspect what splitIntoChunks strips — if it removes markdown, make sure real prose remains before posting to the worker.
- If queueing from the UI, debounce and ignore empty messages instead of posting them to the worker.
Example fix
// before
worker.postMessage({ type: 'stream', streamId, text });
// after
if (!text || !text.trim()) return; // nothing to synthesize
worker.postMessage({ type: 'stream', streamId, text }); Defensive patterns
Strategy: validation
Validate before calling
// run before posting a stream to the piperTTS worker
function hasSpeakableText(text) {
return typeof text === 'string' && text.trim().length > 0;
}
if (!hasSpeakableText(text)) return; // never enqueue empty/whitespace text Type guard
/** @param {any} t @returns {t is string} */
function isSpeakable(t) {
return typeof t === 'string' && t.replace(/\s|[*_`>#-]/g, '').length > 0;
} Try / catch
self.onerror = (e) => {
if (/No text to predict on/.test(e.message)) return; // benign — nothing to speak
postMessage({ type: 'stream-error', message: e.message });
}; Prevention
- Skip TTS for empty or symbol-only messages before posting to the worker.
- Debounce speak-on-arrive features so placeholder chunks are never voiced.
- Treat this error as benign in worker error handling — logging it loudly just adds noise.
When it happens
Trigger: Enqueueing TTS for an empty or whitespace-only string; text that is entirely stripped away by the chunking logic (e.g. only markdown/symbols, no speakable prose); a UI race where the stream is posted before the message text is set.
Common situations: Speaking an empty assistant message; message content that reduces to nothing after sanitization; autoplay/speak-on-arrive features firing on placeholder chunks.
Related errors
- Invalid password.
- No valid user IDs provided.
- Passwords do not match
- search pattern must not start with '-'
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/284327b035c8decb.
Report an issue: GitHub.
Appendix: source
Thrown at frontend/src/utils/piperTTS/worker.js:141
self.postMessage({ type: "error", message: error.message, error }); // Will be an error.
});
}
/**
* Renders one streamed prediction. Every message posted back carries the
* streamId so listeners can ignore chunks that belong to a different stream.
* @param {string} streamId
* @param {string} text
*/
async function runStream(streamId, text) {
try {
if (ACTIVE_STREAM_ID !== streamId) {
// Superseded or aborted while waiting in the queue - never started.
self.postMessage({ type: "stream-end", streamId, aborted: true });
return;
}
const chunks = splitIntoChunks(text);
if (chunks.length === 0) throw new Error("No text to predict on.");
self.postMessage({ type: "stream-start", streamId, total: chunks.length });
for (let i = 0; i < chunks.length; i++) {
if (ACTIVE_STREAM_ID !== streamId) break;
const audio = await PIPER_SESSION.predict(chunks[i]);
if (ACTIVE_STREAM_ID !== streamId) break; // lost ownership mid-predict - drop the stale chunk
self.postMessage({
type: "stream-chunk",
streamId,
audio,
index: i,
total: chunks.length,
});
}
self.postMessage({
type: "stream-end",
streamId,
aborted: ACTIVE_STREAM_ID !== streamId,
});View on GitHub (pinned to 3aec848f28)