janhq/jan · error · Error

No API key configured for ${provider.provider}. Add one in S

Error message

No API key configured for ${provider.provider}. Add one in Settings > Model Providers.

What it means

Thrown by requireRemoteApiKey when both the keyChain passed in and provider.api_key are empty/missing. The preceding comment explains the motivation: an empty key would still send an empty auth header, which upstreams answer with misleading 401s (e.g. Anthropic's 'x-api-key header is required'). This guard fails fast with an actionable message instead (model-factory.ts:791).

Source

Thrown at web-app/src/lib/model-factory.ts:791

        res.body?.cancel().catch(() => {})
        continue
      }
      return res
    }
    throw new Error('API key rotation exhausted')
  }
}

// An empty apiKey still puts an empty auth header on the wire, which upstreams
// answer with misleading 401s (e.g. Anthropic's "x-api-key header is
// required"). Fail here with an actionable message instead.
function requireRemoteApiKey(
  provider: ProviderObject,
  keyChain: string[]
): string {
  const key = keyChain[0] ?? provider.api_key?.trim()
  if (!key) {
    throw new Error(
      `No API key configured for ${provider.provider}. Add one in Settings > Model Providers.`
    )
  }
  return key
}

function getRuntimeFetch(): typeof globalThis.fetch {
  const maybeWindow = globalThis as typeof globalThis & {
    __TAURI__?: unknown
    __TAURI_INTERNALS__?: unknown
  }
  const hasTauriRuntime =
    typeof maybeWindow.__TAURI__ !== 'undefined' ||
    typeof maybeWindow.__TAURI_INTERNALS__ !== 'undefined'

  return isPlatformTauri() && hasTauriRuntime
    ? (httpFetch as typeof globalThis.fetch)
    : globalThis.fetch

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Go to Settings > Model Providers, select the provider named in the message, and paste a valid API key.
  2. If using the OS keyring, ensure the app has keychain access (macOS: allow in System Settings; Linux: unlock the keyring daemon).
  3. Verify the key is not whitespace-only — the guard trims, so leading/trailing spaces produce the same failure.
  4. Re-select the provider in the UI so the provider object is rehydrated with the freshly stored key.

Example fix

// before
const key = keyChain[0] ?? provider.api_key?.trim()
if (!key) throw new Error(`No API key configured for ${provider.provider}...`)
// after: also clear empty keychain entries upstream so the UI disables Send
if (!key) {
  setErrorState({ code: 'MISSING_API_KEY', provider: provider.provider })
  throw new Error(`No API key configured for ${provider.provider}. Add one in Settings > Model Providers.`)
}
Defensive patterns

Strategy: validation

Validate before calling

function hasRemoteApiKey(provider: ProviderObject, keyChain: string[]): boolean {
  const k = keyChain[0] ?? provider.api_key?.trim()
  return Boolean(k)
}
// before calling requireRemoteApiKey:
if (!hasRemoteApiKey(provider, keyChain)) {
  setNeedsKey(provider.provider)
  return
}

Type guard

function providerHasApiKey(p: ProviderObject): boolean {
  return Boolean(p.api_key && p.api_key.trim().length > 0)
}

Try / catch

try {
  const key = requireRemoteApiKey(provider, keyChain)
  // ... build model
} catch (e) {
  if (e instanceof Error && e.message.startsWith('No API key configured')) {
    openSettings('model-providers', provider.provider)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: A remote/cloud provider (not llamacpp/mlx/local) is selected for a chat, ModelFactory.createModel calls requireRemoteApiKey, and provider.api_key is undefined/empty while the keyChain array is empty or its first entry is falsy.

Common situations: First run with a cloud provider before the user entered a key; the key was saved to the OS keyring but keyring access was denied so keyChain came back empty; a stale provider object persisted in state after the user cleared credentials; whitespace-only key stored.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/41fca4fdfeafaede. Report an issue: GitHub.