decolua/9router · error · Error

${provider} API key required

Error message

${provider} API key required

What it means

`synthesizeViaConfig` resolves the provider's TTS config from AI_PROVIDERS and its credentials. For providers whose `ttsConfig.authType` is not `none`, an API key is mandatory; missing credentials throw `${provider} API key required`. This is a client-side configuration gate before any upstream call.

Source

Thrown at open-sse/handlers/ttsProviders/index.js:39

  openrouter,
  gemini,
  "xiaomi-mimo": xiaomiMimo,
  "selfhosted-tts": selfhostedTts,
};

export function getTtsAdapter(provider) {
  return SPECIAL_ADAPTERS[provider] || null;
}

// Generic config-driven dispatcher (uses ttsConfig.format)
export async function synthesizeViaConfig(provider, text, model, credentials) {
  const { AI_PROVIDERS } = await import("@/shared/constants/providers");
  const cfg = AI_PROVIDERS[provider]?.ttsConfig;
  if (!cfg) return null;
  const handler = FORMAT_HANDLERS[cfg.format];
  if (!handler) return null;
  const apiKey = credentials?.apiKey;
  if (cfg.authType !== "none" && !apiKey) throw new Error(`${provider} API key required`);
  const { PROVIDER_MODELS } = await import("open-sse/config/providerModels.js");
  const ttsModels = (PROVIDER_MODELS[provider] || []).filter(m => (m.kind || m.type) === "tts");
  const defaultModel = ttsModels[0]?.id || "";
  const { modelId, voiceId } = parseModelVoice(model, defaultModel, "", ttsModels);
  return handler({ baseUrl: cfg.baseUrl, apiKey, text, modelId, voiceId });
}

// Voice fetchers (used by /api/media-providers/tts/voices route)
export const VOICE_FETCHERS = {
  "edge-tts": fetchEdgeTtsVoices,
  "local-device": fetchLocalDeviceVoices,
  elevenlabs: fetchElevenLabsVoices,
  gemini: fetchGeminiVoices,
};

// Re-export for backward compat
export { fetchEdgeTtsVoices, fetchLocalDeviceVoices, fetchElevenLabsVoices, fetchGeminiVoices };

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Configure the provider's API key in the dashboard credential store (or the credentials object passed to synthesize)
  2. Verify AI_PROVIDERS[provider].ttsConfig.authType — only `none` providers (like the Google scrape path) work keyless
  3. Check the key is stored under the field the credentials resolver reads (`apiKey`), not a differently named field
  4. If you intended a free/local TTS, switch the model/provider to one with authType `none`

Example fix

// before
await synthesizeViaConfig({ provider: 'elevenlabs', text: 'hi', credentials: {} });
// after
await synthesizeViaConfig({ provider: 'elevenlabs', text: 'hi', credentials: { apiKey: process.env.ELEVENLABS_API_KEY } });
Defensive patterns

Strategy: validation

Validate before calling

const cfg = AI_PROVIDERS[provider]?.ttsConfig;
if (cfg && cfg.authType !== 'none' && !credentials?.apiKey) {
  throw new Error(`${provider} API key must be configured before TTS calls`);
}

Type guard

const hasApiKey = (c) => c != null && typeof c.apiKey === 'string' && c.apiKey.length > 0;

Try / catch

try { return await synthesizeViaConfig(opts); } catch (e) { if (e.message.endsWith('API key required')) { throw new ConfigError(`Set an API key for ${provider} in the dashboard`); } throw e; }

Prevention

When it happens

Trigger: Calling synthesize for a TTS provider (e.g. elevenlabs, cartesia, inworld) whose stored credentials have no `apiKey`, while that provider's ttsConfig.authType is anything other than `none`.

Common situations: Fresh install with no credentials configured; credentials saved for chat completions but the TTS-specific key field empty; using a provider like the free Google translate path whose authType is `none` vs a keyed provider confused in config; env var not imported into the credential store.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/383eead0ed5150e3. Report an issue: GitHub.