{"record":{"id":"e6f6734c1931003a","repo":"HeyPuter/puter","slug":"bad-request-e6f673","errorCode":"bad_request","errorMessage":"TTS provider not configured: ${providerName}. Available: ${Object.keys(this.#providers).join(', ')}","messagePattern":"TTS provider not configured: (.+?)\\. Available: (.+?)","errorType":"validation","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/drivers/ai-tts/TTSDriver.ts","lineNumber":163,"sourceCode":"    }\n\n    /**\n     * Synthesize speech from text, routed to the provider named by `provider`\n     * (or the default when none is given).\n     */\n    async synthesize(\n        args: ISynthesizeArgs,\n    ): Promise<DriverStreamResult | { url: string; content_type: string }> {\n        const actor = Context.get('actor');\n        if (!actor)\n            throw new HttpError(401, 'Authentication required', {\n                legacyCode: 'unauthorized',\n            });\n\n        const providerName = this.#resolveProvider(args);\n        const provider = this.#providers[providerName];\n        if (!provider) {\n            throw new HttpError(\n                400,\n                `TTS provider not configured: ${providerName}. Available: ${Object.keys(this.#providers).join(', ')}`,\n                { legacyCode: 'bad_request' },\n            );\n        }\n\n        return provider.synthesize(\n            this.#providerArgs(providerName, args),\n        ) as Promise<\n            DriverStreamResult | { url: string; content_type: string }\n        >;\n    }\n\n    // -- Provider routing --------------------------------------------\n\n    /**\n     * Decide which provider handles a call. An explicit `provider` wins, then\n     * an `engine` that names a provider (a long-standing shorthand), then the","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-tts/TTSDriver.ts#L145-L181","documentation":"After #resolveProvider returns a canonical provider name, TTSDriver looks it up in #providers (the providers actually instantiated from config). If the name is valid but no provider instance exists — because its credentials were never supplied or its constructor threw during boot — it throws HTTP 400 (legacyCode bad_request), listing the providers that ARE configured. Distinct from 425: here the name normalized fine; it just isn't configured.","triggerScenarios":"Requesting provider 'aws-polly' when no access_key/secret_key were given; 'elevenlabs' with no apiKey; 'gemini' with no key; or any provider whose constructor threw during #registerProviders (logged as a console.warn on boot).","commonSituations":"Default provider is aws-polly but the deployment only configured openai — any call that resolves to aws-polly fails; a key was typo'd so construction logged a warning and the provider stayed unregistered; requesting a provider by alias that maps to an unconfigured canonical name.","solutions":["Supply credentials for the requested provider in config.providers (e.g. aws-polly needs aws.access_key and aws.secret_key).","Call the driver's list() method first to see which providers are actually configured, then route to one of them.","Omit the provider argument to let #defaultProvider pick a configured one.","Check boot logs for '[TTSDriver] Failed to init ... provider' warnings indicating a construction failure."],"exampleFix":"// before\nawait driver.synthesize({ text: 'hi', provider: 'aws-polly' }); // not configured\n// after — let the default (or a configured provider) handle it\nawait driver.synthesize({ text: 'hi' });","handlingStrategy":"validation","validationCode":"// Ask the driver which providers are actually configured, then pick one.\nconst configured = await driver.list(); // returns Object.keys(#providers)\nif (!configured.includes(targetProvider)) {\n  // fall back to a configured provider or surface 'unavailable'\n}\nawait driver.synthesize({ text: 'hi', provider: configured[0] });","typeGuard":null,"tryCatchPattern":"try {\n  await driver.synthesize({ text: 'hi', provider: name });\n} catch (e) {\n  if (e?.fields?.legacyCode === 'bad_request' && /not configured/.test(e.message)) {\n    const available = await driver.list();\n    // retry with an available provider or notify the user\n  } else throw e;\n}","preventionTips":["Call list() at app start to learn which providers are live.","Omit provider to let #defaultProvider choose a configured one.","Check boot logs for 'Failed to init ... provider' warnings that explain why a provider is missing."],"tags":["config","tts","provider-routing","credentials"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}