HeyPuter/puter · error · HttpError

insufficient_funds

insufficient_funds

Error message

Insufficient funds

What it means

Credit gate in ElevenLabsTTSProvider.synthesize. totalCost = ELEVENLABS_TTS_COSTS[modelId] * text.length; if meteringService.hasEnoughCredits(actor, totalCost) is false, it throws HTTP 402 (legacyCode insufficient_funds). Billing is per character, and different models carry different per-character rates.

Source

Thrown at src/backend/drivers/ai-tts/providers/elevenlabs/ElevenLabsTTSProvider.ts:248

                    fields: { key: 'model', expected, got: modelId },
                },
            );
        }

        const desiredFormat =
            output_format || response_format || DEFAULT_OUTPUT_FORMAT;

        const actor = Context.get('actor')!;
        const usageKey = `elevenlabs:${modelId}:character`;
        const ucentsPerChar = ELEVENLABS_TTS_COSTS[modelId];
        const totalCost = ucentsPerChar * text.length;

        const usageAllowed = await this.meteringService.hasEnoughCredits(
            actor,
            totalCost,
        );
        if (!usageAllowed) {
            throw new HttpError(402, 'Insufficient funds', {
                legacyCode: 'insufficient_funds',
            });
        }

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const payload: any = {
            text,
            model_id: modelId,
            output_format: desiredFormat,
        };

        const finalVoiceSettings = voice_settings ?? voiceSettings;
        if (finalVoiceSettings) {
            payload.voice_settings = finalVoiceSettings;
        }

        const response = await this.request(`/v1/text-to-speech/${voiceId}`, {
            method: 'POST',

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Top up the actor/workspace balance.
  2. Shorten the text or batch it.
  3. Switch to a cheaper model (e.g. eleven_flash_v2_5).
  4. Pre-estimate cost as chars * ELEVENLABS_TTS_COSTS[model] before submitting.
Defensive patterns

Strategy: try-catch

Validate before calling

// Estimate ElevenLabs cost (per-character) before calling.
const ELEVENLABS_TTS_COSTS = {
  'eleven_multilingual_v2': 190,
  'eleven_flash_v2_5': 50,
  'eleven_turbo_v2_5': 100,
  'eleven_v3': 300,
};
function estimateUcents(text, model) {
  return ELEVENLABS_TTS_COSTS[model] * text.length;
}
// Compare to the user's balance; switch to a cheaper model if it won't fit.

Try / catch

try {
  await driver.synthesize({ text, provider: 'elevenlabs', model });
} catch (e) {
  if (e?.status === 402 || e?.fields?.legacyCode === 'insufficient_funds') {
    // prompt top-up, or retry on eleven_flash_v2_5 with the same text
  } else throw e;
}

Prevention

When it happens

Trigger: An actor whose balance is below the per-character cost of text on the chosen model. Premium models (e.g. eleven_v3) cost more per character, so the same text can pass on eleven_flash_v2_5 but fail on a pricier model.

Common situations: Long-form narration on a premium model exhausting a small balance; free-tier users hitting limits; switching to a costlier model without budget awareness.

Related errors


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