{"record":{"id":"adb950138ca9b052","repo":"danny-avila/LibreChat","slug":"message-id-is-required","errorCode":null,"errorMessage":"Message ID is required","messagePattern":"Message ID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/server/services/Files/Audio/streamAudio.js","lineNumber":67,"sourceCode":" * @property {number[]} normalizedAlignment.char_start_times_ms\n * @property {number[]} normalizedAlignment.chars_durations_ms\n * @property {string[]} normalizedAlignment.chars\n */\n\nconst MAX_NOT_FOUND_COUNT = 6;\nconst MAX_NO_CHANGE_COUNT = 10;\n\n/**\n * @param {string} user\n * @param {string} messageId\n * @returns {() => Promise<{ text: string, isFinished: boolean }[]>}\n */\nfunction createChunkProcessor(user, messageId) {\n  let notFoundCount = 0;\n  let noChangeCount = 0;\n  let processedText = '';\n  if (!messageId) {\n    throw new Error('Message ID is required');\n  }\n\n  const messageCache = getLogStores(CacheKeys.MESSAGES);\n  // Captured at creation time — must be called within an active request ALS scope\n  const cacheKey = scopedCacheKey(messageId);\n\n  /**\n   * @returns {Promise<{ text: string, isFinished: boolean }[] | string>}\n   */\n  async function processChunks() {\n    if (notFoundCount >= MAX_NOT_FOUND_COUNT) {\n      return `Message not found after ${MAX_NOT_FOUND_COUNT} attempts`;\n    }\n\n    if (noChangeCount >= MAX_NO_CHANGE_COUNT) {\n      return `No change in message after ${MAX_NO_CHANGE_COUNT} attempts`;\n    }\n","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/Files/Audio/streamAudio.js#L49-L85","documentation":"createChunkProcessor requires a messageId up front because it builds the messages-cache key (scopedCacheKey(messageId)) used to poll streaming TTS chunks. It throws synchronously at construction time, before any async work, since without an id there is nothing to poll. This is a programmer/contract error, not a user input error.","triggerScenarios":"A caller invokes createChunkProcessor(user, messageId) with messageId undefined, null, or empty string — typically because the message had not been persisted yet or the request payload omitted the id.","commonSituations":"TTS streaming initiated before the parent message was saved; frontend request missing the messageId field; race where message creation hasn't resolved; refactor that dropped the argument.","solutions":["Ensure the messageId is created and non-empty before invoking createChunkProcessor.","Persist the message first, then start the chunk processor with the resulting id.","Add a caller-side guard so an absent id short-circuits before reaching this function."],"exampleFix":"// before\nconst processor = createChunkProcessor(user, req.body.messageId);\n\n// after\nif (!req.body.messageId) {\n  return res.status(400).json({ error: 'messageId is required' });\n}\nconst processor = createChunkProcessor(user, req.body.messageId);","handlingStrategy":"validation","validationCode":"if (!messageId || typeof messageId !== 'string') {\n  throw new Error('messageId must be a non-empty string');\n}\nconst processor = createChunkProcessor(user, messageId);","typeGuard":"/** @param {unknown} id */\nfunction isValidMessageId(id) {\n  return typeof id === 'string' && id.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Persist the message and obtain its id before starting the chunk processor.","Validate the request payload's messageId at the route boundary.","Treat a missing id as a 400, not as something to retry."],"tags":["tts","streaming","programming-error","precondition"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}