HeyPuter/puter · error · HttpError

field_invalid

field_invalid

Error message

Invalid model: ${model}. Expected: ${GEMINI_TTS_MODELS.map(({ id }) => id).join(', ')}

What it means

GeminiTTSProvider.synthesize resolves model (defaulting to gemini-2.5-flash-preview-tts) and checks it against GEMINI_TTS_MODELS. If the id is not found, it throws HTTP 400 (legacyCode field_invalid) with fields.key='model', fields.expected (the supported ids), and fields.got.

Source

Thrown at src/backend/drivers/ai-tts/providers/gemini/GeminiTTSProvider.ts:154

            model: modelArg,
            instructions,
            test_mode,
        } = args;

        if (test_mode) {
            return { url: SAMPLE_AUDIO_URL, content_type: 'audio' };
        }

        if (typeof text !== 'string' || !text.trim()) {
            throw new HttpError(400, 'Missing required field: text', {
                legacyCode: 'field_required',
                fields: { key: 'text' },
            });
        }

        const model = modelArg || DEFAULT_MODEL;
        if (!GEMINI_TTS_MODELS.find(({ id }) => id === model)) {
            throw new HttpError(
                400,
                `Invalid model: ${model}. Expected: ${GEMINI_TTS_MODELS.map(({ id }) => id).join(', ')}`,
                {
                    legacyCode: 'field_invalid',
                    fields: {
                        key: 'model',
                        expected: GEMINI_TTS_MODELS.map(({ id }) => id).join(
                            ', ',
                        ),
                        got: model,
                    },
                },
            );
        }

        const voice = voiceArg || DEFAULT_VOICE;
        if (
            !GEMINI_TTS_VOICES.find(

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Use a GEMINI_TTS_MODELS id: gemini-2.5-flash-preview-tts, gemini-2.5-pro-preview-tts, gemini-3.1-flash-tts-preview (read fields.expected).
  2. Omit model to accept the default (gemini-2.5-flash-preview-tts).
  3. Call list_engines({ provider: 'gemini' }) to fetch supported ids.

Example fix

// before
await driver.synthesize({ text: 'hi', provider: 'gemini', model: 'gemini-1.5-flash' });
// after
await driver.synthesize({ text: 'hi', provider: 'gemini', model: 'gemini-2.5-flash-preview-tts' });
Defensive patterns

Strategy: validation

Validate before calling

const GEMINI_TTS_MODELS = ['gemini-2.5-flash-preview-tts', 'gemini-2.5-pro-preview-tts', 'gemini-3.1-flash-tts-preview'];
function synthesizeGemini(text, model = 'gemini-2.5-flash-preview-tts') {
  if (!GEMINI_TTS_MODELS.includes(model)) {
    throw new Error(`Unsupported Gemini TTS model: ${model}. Use one of: ${GEMINI_TTS_MODELS.join(', ')}`);
  }
  return driver.synthesize({ text, provider: 'gemini', model });
}

Type guard

const GEMINI_TTS_MODELS = ['gemini-2.5-flash-preview-tts', 'gemini-2.5-pro-preview-tts', 'gemini-3.1-flash-tts-preview'] as const;
type GeminiTTSModel = typeof GEMINI_TTS_MODELS[number];

const isGeminiTTSModel = (v: unknown): v is GeminiTTSModel =>
  typeof v === 'string' && (GEMINI_TTS_MODELS as readonly string[]).includes(v);

Prevention

When it happens

Trigger: Passing model: 'gemini-1.5-flash' (a non-TTS Gemini model) or any id absent from GEMINI_TTS_MODELS. Only the specific '-tts' preview models are accepted.

Common situations: Using a chat/completion Gemini model id for TTS; passing a deprecated TTS model after Google renames it; typo; assuming any Gemini model can emit audio.

Related errors


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