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 when splitIntoChunks(text) returns an empty array, meaning there is no speakable content. The worker runs predictions chunk-by-chunk against PIPER_SESSION.predict; with zero chunks there is nothing to synthesize, so it throws before posting stream-start. The catch posts an { type: 'error', streamId, message } back to the main thread.

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 526360e320)

Solutions

  1. Trim and check the text is non-empty (has at least one alphanumeric character) before posting the message to the worker.
  2. Adjust splitIntoChunks so it treats punctuation-only input as content rather than dropping it.
  3. In the main thread, ignore the worker's 'error' message when the message field is 'No text to predict on.' since it is benign.

Example fix

// before — caller posts unconditionally
worker.postMessage({ type: 'stream-start', streamId, text });

// after — guard empty text in the caller
const speakable = text.trim();
if (!speakable) return;
worker.postMessage({ type: 'stream-start', streamId, text: speakable });
Defensive patterns

Strategy: validation

Validate before calling

// Only post to the TTS worker if there is speakable text.
function hasSpeakableText(text) {
  return typeof text === 'string' && /\S/.test(text.replace(/[\s\p{P}]/gu, ''));
}

Try / catch

// In the worker, downgrade empty-text to a benign stream-end rather than an error.
if (chunks.length === 0) {
  self.postMessage({ type: 'stream-end', streamId, aborted: true });
  return;
}

Prevention

When it happens

Trigger: Calling the worker's runStream with an empty string, a string that is only whitespace/punctuation that splitIntoChunks strips, or calling runStream after SSML/markdown sanitization removed all tokens.

Common situations: TTS triggered on a chat message that contains only an image or code block (no prose); user clicked 'speak' on an empty input; upstream text sanitizer over-aggressively removed punctuation-only runs; clipboard paste of whitespace-only content.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13). Data as JSON: /api/errors/284327b035c8decb. Report an issue: GitHub.