HeyPuter/puter · error · HttpError
bad_request
bad_request
Error message
Speech-to-speech provider not found: ${args.provider}. Available: ${PROVIDERS.join(', ')} What it means
The voice-changer driver (puter-speech2speech) only ships an ElevenLabs provider. The caller set args.provider to a value that, after trim+lowercase, is not in the single-entry allowlist (['elevenlabs']), so the driver refuses rather than silently route to ElevenLabs. The check exists so that once a second provider lands, a stale provider value fails loud instead of converting with the wrong backend. Thrown as 400 / bad_request.
Source
Thrown at src/backend/drivers/ai-speech2speech/VoiceChangerDriver.ts:121
this.#defaultModelId =
(elevenlabs?.speechToSpeechModelId as string | undefined) ??
DEFAULT_MODEL;
}
async convert(
args: ConvertArgs,
): Promise<DriverStreamResult | { url: string; content_type: string }> {
// Only one provider exists today, but naming a different one should
// fail loudly rather than quietly convert with this one.
if (
args.provider &&
!PROVIDERS.includes(
args.provider
.trim()
.toLowerCase() as (typeof PROVIDERS)[number],
)
) {
throw new HttpError(
400,
`Speech-to-speech provider not found: ${args.provider}. Available: ${PROVIDERS.join(', ')}`,
{ legacyCode: 'bad_request' },
);
}
if (args.test_mode) {
return { url: SAMPLE_AUDIO_URL, content_type: 'audio/mpeg' };
}
if (!this.#apiKey) {
throw new HttpError(500, 'ElevenLabs API key not configured', {
legacyCode: 'internal_error',
});
}
const actor = Context.get('actor');
if (!actor)View on GitHub (pinned to 908ec23eda)
Solutions
- Pass provider: 'elevenlabs' (case/space tolerant — it is trimmed and lowercased) or simply omit it; the driver defaults to ElevenLabs.
- Confirm the driver interface you dispatched to is puter-speech2speech. TTS and STT have their own, different provider lists.
Example fix
// before
driver.convert({ audio, provider: 'openai' })
// after
driver.convert({ audio }) // or provider: 'elevenlabs' Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_S2S = ['elevenlabs'];
if (args.provider && !ALLOWED_S2S.includes(String(args.provider).trim().toLowerCase())) {
throw new Error(`provider must be one of: ${ALLOWED_S2S.join(', ')}`);
} Prevention
- Omit provider to use the driver default; the only valid value is 'elevenlabs'.
- Mirror the driver's allowlist client-side before the network call.
- Don't forward provider names copied from TTS or chat-completion calls.
When it happens
Trigger: Calling driver.convert({ audio, provider: 'openai' }) (or 'azure', 'google', any typo) on the puter-speech2speech driver. Also hit by an older SDK bundle that injects a provider name copied from a sibling AI call (TTS/chat).
Common situations: Reusing args from a text-to-speech or chat-completion call into the speech-to-speech driver; integration tests stubbing a fake provider name; a puter.js bundle that names 'azure' or 'google'.
Related errors
- bad_request
- ${key} must be an integer between ${min} and ${max}
- App owner not found
- Permission denied
- Authentication required
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/8b39ee1f9347f775.
Report an issue: GitHub.