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

  1. Pass provider: 'elevenlabs' (case/space tolerant — it is trimmed and lowercased) or simply omit it; the driver defaults to ElevenLabs.
  2. 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

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


AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12). Data as JSON: /api/errors/8b39ee1f9347f775. Report an issue: GitHub.