{"record":{"id":"f97fe609caa8e63a","repo":"QuantumNous/new-api","slug":"no-models-fetched-from-upstream","errorCode":null,"errorMessage":"No models fetched from upstream","messagePattern":"No models fetched from upstream","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx","lineNumber":1482,"sourceCode":"    const type = form.getValues('type')\n    const editingAdvancedCustom =\n      isEditing && type === CHANNEL_TYPE_ADVANCED_CUSTOM\n    if (editingAdvancedCustom && channelId === null) {\n      throw new Error(t('No channel selected'))\n    }\n    const response = await fetchModels({\n      type,\n      key: isEditing ? undefined : form.getValues('key'),\n      channel_id: editingAdvancedCustom ? channelId || undefined : undefined,\n      base_url: form.getValues('base_url') || '',\n      advanced_custom: form.getValues('advanced_custom'),\n      header_override: form.getValues('header_override'),\n      proxy: form.getValues('proxy'),\n    })\n    if (response.success && response.data) {\n      return response.data\n    }\n    throw new Error(response.message || t('No models fetched from upstream'))\n  }, [canEditSensitive, channelId, form, isEditing, t])\n\n  // Handle model operations\n  const handleFillRelatedModels = useCallback(() => {\n    if (!basicModels.length) {\n      toast.info(t('No related models available for this channel type'))\n      return\n    }\n    updateModels(basicModels)\n    toast.success(\n      t('Filled {{count}} related model(s)', { count: basicModels.length })\n    )\n  }, [basicModels, updateModels, t])\n\n  const handleFillAllModels = useCallback(() => {\n    if (!allModelsList.length) {\n      toast.info(t('No models available'))\n      return","sourceCodeStart":1464,"sourceCodeEnd":1500,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L1464-L1500","documentation":"Thrown by formPreviewFetcher after fetchModels resolves without success or without data. The request carries type, key (only when creating), channel_id (advanced custom editing), base_url, advanced_custom, header_override, and proxy; any upstream failure to list models ends here with the backend message or this i18n fallback.","triggerScenarios":"Clicking fetch models when the upstream /v1/models call fails: wrong or invalid key, unreachable base_url, bad proxy setting, header_override breaking auth, or upstream returning an empty/error payload with success=true but no data.","commonSituations":"Key entered but not yet saved / typo'd; base_url missing scheme or pointing at a non-OpenAI-compatible endpoint; corporate proxy field wrong so the backend cannot connect; provider renamed the models endpoint.","solutions":["Double-check key and base_url in the form (the exact values sent are the form fields shown in the source)","Test connectivity from the backend host: curl {base_url}/v1/models with the same key and proxy","Temporarily clear header_override/advanced_custom to isolate which field breaks the request","Read the backend message in the error toast — it usually contains the upstream status/body"],"exampleFix":"// before\nif (response.success && response.data) {\n  return response.data\n}\nthrow new Error(response.message || t('No models fetched from upstream'))\n// after - distinguish empty list from failure explicitly\nif (!response.success) {\n  throw new Error(response.message || t('No models fetched from upstream'))\n}\nif (!response.data?.length) {\n  throw new Error(t('No models fetched from upstream'))\n}\nreturn response.data","handlingStrategy":"try-catch","validationCode":"if (!canEditSensitive) throw new Error(t(\"You don't have necessary permission\"))\nconst key = form.getValues('key')\nconst needsKey = !isEditing || !!form.getValues('base_url')\nif (needsKey && !key?.trim()) { toast.error(t('Please enter API key first')); return }","typeGuard":"const isModelList = (d: unknown): d is string[] =>\n  Array.isArray(d) && d.every((m) => typeof m === 'string')","tryCatchPattern":"try {\n  const response = await fetchModels({ type, key: isEditing ? undefined : form.getValues('key'), channel_id: editingAdvancedCustom ? channelId || undefined : undefined, base_url: form.getValues('base_url') || '', advanced_custom: form.getValues('advanced_custom'), header_override: form.getValues('header_override'), proxy: form.getValues('proxy') })\n  if (!response.success || !response.data?.length) {\n    throw new Error(response.message || t('No models fetched from upstream'))\n  }\n  return response.data\n} catch (error) {\n  toast.error(error instanceof Error ? error.message : t('No models fetched from upstream'))\n  return []\n}","preventionTips":["Require a non-empty key (create mode) and scheme-absolute base_url before fetching","Distinguish 'request failed' from 'empty list' in the thrown message","Clear header_override when debugging to isolate auth-breaking headers"],"tags":["api-error","fetch-models","upstream","channels"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}