{"record":{"id":"f64fdfe84a3cc16c","repo":"danny-avila/LibreChat","slug":"text-is-required","errorCode":null,"errorMessage":"Text is required","messagePattern":"Text is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/server/services/Files/Audio/streamAudio.js","lineNumber":145,"sourceCode":"    } else if (complete && remainingText.trim().length > 0) {\n      chunks.push({ text: remainingText.trim(), isFinished: true });\n      processedText = text;\n    }\n\n    return chunks;\n  }\n\n  return processChunks;\n}\n\n/**\n * @param {string} text\n * @param {number} [chunkSize=4000]\n * @returns {{ text: string, isFinished: boolean }[]}\n */\nfunction splitTextIntoChunks(text, chunkSize = 4000) {\n  if (!text) {\n    throw new Error('Text is required');\n  }\n\n  const chunks = [];\n  let startIndex = 0;\n  const textLength = text.length;\n\n  while (startIndex < textLength) {\n    let endIndex = Math.min(startIndex + chunkSize, textLength);\n    let chunkText = text.slice(startIndex, endIndex);\n\n    if (endIndex < textLength) {\n      let lastSeparatorIndex = -1;\n      for (const separator of SEPARATORS) {\n        const index = chunkText.lastIndexOf(separator);\n        if (index !== -1) {\n          lastSeparatorIndex = Math.max(lastSeparatorIndex, index);\n        }\n      }","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/Files/Audio/streamAudio.js#L127-L163","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst chunks = splitTextIntoChunks(text);\n\n// after\nif (!text || !text.trim()) {\n  return res.status(400).json({ message: 'Text is required' });\n}\nconst chunks = splitTextIntoChunks(text);","handlingStrategy":"validation","validationCode":"// Before calling the TTS/streamAudio pipeline\nfunction hasSpeakableText(text) {\n  return typeof text === 'string' && text.trim().length > 0;\n}\n\nif (!hasSpeakableText(message?.text)) {\n  return res.status(400).json({ message: 'Text is required' });\n}\nsplitTextIntoChunks(message.text);","typeGuard":"/** @param {unknown} t @returns {t is string} */\nfunction isNonEmptyString(t) {\n  return typeof t === 'string' && t.trim().length > 0;\n}","tryCatchPattern":"try {\n  const chunks = splitTextIntoChunks(text);\n} catch (err) {\n  if (/Text is required/.test(err.message)) {\n    return res.status(400).json({ message: 'Cannot synthesize empty text' });\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["validation","tts","audio","input-validation"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}