{"record":{"id":"2d5399e3d2c78931","repo":"danny-avila/LibreChat","slug":"configuration-or-tts-schema-is-missing","errorCode":null,"errorMessage":"Configuration or TTS schema is missing","messagePattern":"Configuration or TTS schema is missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/server/services/Files/Audio/getVoices.js","lineNumber":27,"sourceCode":" *\n * @param {Object} req - The request object\n * @param {Object} res - The response object\n * @returns {Promise<void>}\n * @throws {Error} - If the provider is not 'openai' or 'elevenlabs', an error is thrown\n */\nasync function getVoices(req, res) {\n  try {\n    const appConfig =\n      req.config ??\n      (await getAppConfig({\n        role: req.user?.role,\n        userId: req.user?.id,\n        tenantId: req.user?.tenantId,\n      }));\n\n    const ttsSchema = appConfig?.speech?.tts;\n    if (!ttsSchema) {\n      throw new Error('Configuration or TTS schema is missing');\n    }\n\n    const provider = await getProvider(appConfig);\n    let voices;\n\n    switch (provider) {\n      case TTSProviders.OPENAI:\n        voices = ttsSchema.openai?.voices;\n        break;\n      case TTSProviders.AZURE_OPENAI:\n        voices = ttsSchema.azureOpenAI?.voices;\n        break;\n      case TTSProviders.ELEVENLABS:\n        voices = ttsSchema.elevenlabs?.voices;\n        break;\n      case TTSProviders.LOCALAI:\n        voices = ttsSchema.localai?.voices;\n        break;","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/Files/Audio/getVoices.js#L9-L45","documentation":"The getVoices endpoint guard: after resolving appConfig it checks appConfig.speech.tts and throws this distinct message before delegating to TTSService.getProvider. It exists so the endpoint can return a 500 with a clear cause rather than the underlying 'No TTS schema' text. Same root cause as the TTSService schema-missing error but surfaced at the route layer.","triggerScenarios":"GET /api/voices when librechat.yaml has no `speech.tts` section, or role/tenant-scoped config returns an object whose `speech.tts` is falsy.","commonSituations":"Client renders a voice picker before any TTS provider is configured; librechat.yaml not mounted; tenant override returns empty speech.","solutions":["Configure a `speech.tts` block with one populated provider in librechat.yaml.","Verify the config file is mounted and loaded (startup log).","Restart the API server; re-request /api/voices."],"exampleFix":"# before\nspeech: {}\n\n# after\nspeech:\n  tts:\n    openai:\n      apiKey: '${TTS_API_KEY}'\n      voices: ['alloy', 'echo']","handlingStrategy":"validation","validationCode":"const tts = appConfig?.speech?.tts;\nif (!tts) {\n  return res.status(503).json({ error: 'TTS is not configured on this server.' });\n}","typeGuard":"/** @param {unknown} c */\nfunction hasTtsSchema(c) {\n  return !!c && typeof c === 'object' && 'speech' in c\n    && !!c.speech && typeof c.speech === 'object'\n    && 'tts' in c.speech && !!c.speech.tts && typeof c.speech.tts === 'object';\n}","tryCatchPattern":null,"preventionTips":["Gate the /api/voices route on the presence of speech.tts and return a 503 instead of letting it throw.","Mount and validate librechat.yaml at startup.","Hide the voice picker in the client when TTS is unconfigured."],"tags":["tts","config","librechat-yaml","endpoint"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}