danny-avila/LibreChat · error
Text is required
Error message
Text is required
What it means
Thrown by splitTextIntoChunks in the TTS audio streaming path (streamAudio.js) when the input text is falsy. The chunker is the gate before audio synthesis, so it refuses to operate on empty/null/undefined input rather than send a malformed request to the TTS provider.
Source
Thrown at api/server/services/Files/Audio/streamAudio.js:145
} else if (complete && remainingText.trim().length > 0) {
chunks.push({ text: remainingText.trim(), isFinished: true });
processedText = text;
}
return chunks;
}
return processChunks;
}
/**
* @param {string} text
* @param {number} [chunkSize=4000]
* @returns {{ text: string, isFinished: boolean }[]}
*/
function splitTextIntoChunks(text, chunkSize = 4000) {
if (!text) {
throw new Error('Text is required');
}
const chunks = [];
let startIndex = 0;
const textLength = text.length;
while (startIndex < textLength) {
let endIndex = Math.min(startIndex + chunkSize, textLength);
let chunkText = text.slice(startIndex, endIndex);
if (endIndex < textLength) {
let lastSeparatorIndex = -1;
for (const separator of SEPARATORS) {
const index = chunkText.lastIndexOf(separator);
if (index !== -1) {
lastSeparatorIndex = Math.max(lastSeparatorIndex, index);
}
}View on GitHub (pinned to 5ff282f900)
Solutions
- Validate that the message has non-empty text before invoking the TTS/streamAudio path (guard at the controller/route level).
- Coalesce falsy text to a safe default or short-circuit the request with a 400 when text is empty.
- Trace the caller of splitTextIntoChunks to confirm the text source (message content) is populated before the call.
Example fix
// before
const chunks = splitTextIntoChunks(text);
// after
if (!text || !text.trim()) {
return res.status(400).json({ message: 'Text is required' });
}
const chunks = splitTextIntoChunks(text); Defensive patterns
Strategy: validation
Validate before calling
// Before calling the TTS/streamAudio pipeline
function hasSpeakableText(text) {
return typeof text === 'string' && text.trim().length > 0;
}
if (!hasSpeakableText(message?.text)) {
return res.status(400).json({ message: 'Text is required' });
}
splitTextIntoChunks(message.text); Type guard
/** @param {unknown} t @returns {t is string} */
function isNonEmptyString(t) {
return typeof t === 'string' && t.trim().length > 0;
} Try / catch
try {
const chunks = splitTextIntoChunks(text);
} catch (err) {
if (/Text is required/.test(err.message)) {
return res.status(400).json({ message: 'Cannot synthesize empty text' });
}
throw err;
} Prevention
- Guard at the controller: reject empty message text before reaching the audio path.
- Use a type guard to narrow the message text before passing to the chunker.
- Log the caller stack when this fires to find the upstream source of empty text.
When it happens
Trigger: Calling splitTextIntoChunks(text) (directly or via the streamAudio pipeline) where text is '', null, undefined, 0, or NaN. The guard is a bare `if (!text)` truthiness check, so any falsy value trips it.
Common situations: A TTS request fired for an empty assistant message; upstream message text resolved to undefined because the message wasn't fetched yet; a tool call produced no textual output but still triggered speech.
Related errors
- Voice ${voice} is not available.
- Principal ID is required for user, group, and role principal
- receivedAt must be a non-negative integer timestamp
- Missing required field: prompt
- Missing required field: prompt
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/f64fdfe84a3cc16c.
Report an issue: GitHub.