QuantumNous/new-api · error · Error

Failed to load API key

Error message

Failed to load API key

What it means

Thrown by fetchActiveChatKey in the chat feature when the follow-up request to reveal an API key's plaintext token (fetchTokenKey) fails or returns an empty key field. The list call for keys already succeeded (an enabled key was found), so the failure is specific to the key-reveal endpoint. The thrown message is the server's message when present, otherwise the generic 'Failed to load API key'.

Source

Thrown at web/src/features/chat/hooks/use-active-chat-key.ts:39

import { fetchTokenKey, getApiKeys } from '@/features/keys/api'
import { API_KEY_STATUS } from '@/features/keys/constants'
import { useAuthStore } from '@/stores/auth-store'

export async function fetchActiveChatKey() {
  const result = await getApiKeys({ p: 1, size: 50 })
  if (!result.success) {
    throw new Error(result.message || 'Failed to load API keys')
  }

  const items = result.data?.items ?? []
  const active = items.find((item) => item.status === API_KEY_STATUS.ENABLED)
  if (!active) {
    throw new Error('No enabled API keys found. Create or enable one first.')
  }

  const keyResult = await fetchTokenKey(active.id)
  if (!keyResult.success || !keyResult.data?.key) {
    throw new Error(keyResult.message || 'Failed to load API key')
  }

  return `sk-${keyResult.data.key}`
}

/**
 * Get the currently active API key for chat links
 */
export function useActiveChatKey(enabled: boolean) {
  const userId = useAuthStore((state) => state.auth.user?.id)

  return useQuery({
    queryKey: ['chat-active-key', userId],
    queryFn: fetchActiveChatKey,
    enabled: enabled && Boolean(userId),
    staleTime: 5 * 60 * 1000,
    gcTime: 10 * 60 * 1000,
  })

View on GitHub (pinned to e2c7aa7b10)

Solutions

  1. Check the network tab for the fetchTokenKey request — inspect the HTTP status and response body's success/message fields to see the server's reason.
  2. Verify the authenticated session is still valid and the user owns the key (or is admin) when the reveal endpoint is called.
  3. Retry/refetch via React Query (the hook caches 5 minutes) after fixing the server-side cause; confirm the key still exists in the keys admin page.
  4. If the backend legitimately returns an empty key field, fix the API response to include the key string, or handle keyResult.data?.key === '' explicitly.

Example fix

// before
const keyResult = await fetchTokenKey(active.id)
if (!keyResult.success || !keyResult.data?.key) {
  throw new Error(keyResult.message || 'Failed to load API key')
}

// after — keep the raw server message for diagnosis
const keyResult = await fetchTokenKey(active.id)
if (!keyResult.success) {
  throw new Error(keyResult.message || 'Failed to load API key')
}
if (!keyResult.data?.key) {
  throw new Error('Key reveal returned an empty token for key ' + active.id)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before rendering chat links, confirm a usable key exists
const list = await getApiKeys({ p: 1, size: 50 })
if (!list.success || !(list.data?.items ?? []).some((k) => k.status === API_KEY_STATUS.ENABLED)) {
  // surface 'create or enable a key' UI instead of calling fetchActiveChatKey
}

Type guard

function hasRevealedKey(res: unknown): res is { data: { key: string } } {
  return (
    typeof res === 'object' && res !== null &&
    'success' in res && (res as any).success === true &&
    typeof (res as any).data?.key === 'string' && (res as any).data.key.length > 0
  )
}

Try / catch

try {
  const key = await fetchActiveChatKey()
} catch (error) {
  // React Query captures this in query.error; render a 'create/enable a key' CTA
  // when error.message includes 'No enabled API keys found', else show the message
}

Prevention

When it happens

Trigger: GET /api/token/{id} (fetchTokenKey for the first key with status ENABLED from getApiKeys({p:1,size:50})) returns success:false, or returns success:true but data.key is empty/null/undefined.

Common situations: The key-reveal endpoint requires admin/root or the key owner and the session lost privileges; the key was deleted between the list call and the reveal call; backend returns 200 with success:false for exhausted/disabled keys; reverse proxy strips the route; session expired between the two sequential requests.

Related errors


AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15). Data as JSON: /api/errors/ccd1a37a4d44c312. Report an issue: GitHub.