{"record":{"id":"bdba52023567c8dd","repo":"QuantumNous/new-api","slug":"you-don-t-have-necessary-permission","errorCode":null,"errorMessage":"You don't have necessary permission","messagePattern":"You don't have necessary permission","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx","lineNumber":1462,"sourceCode":"      toast.error(t(\"You don't have necessary permission\"))\n      return\n    }\n\n    // Advanced Custom may use a model discovery route with no authentication.\n    if (!isEditing && type !== CHANNEL_TYPE_ADVANCED_CUSTOM) {\n      const key = form.getValues('key')\n      if (!key?.trim()) {\n        toast.error(t('Please enter API key first'))\n        return\n      }\n    }\n\n    setFetchModelsDialogOpen(true)\n  }, [isEditing, canEditSensitive, form, t])\n\n  const formPreviewFetcher = useCallback(async (): Promise<string[]> => {\n    if (!canEditSensitive) {\n      throw new Error(t(\"You don't have necessary permission\"))\n    }\n    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","sourceCodeStart":1444,"sourceCodeEnd":1480,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L1444-L1480","documentation":"Thrown by formPreviewFetcher in the channel drawer when canEditSensitive is false. Fetching the upstream model list can leak the channel key, so the UI refuses to build the preview request for users without sensitive-field edit permission; the error propagates to the fetch-models dialog.","triggerScenarios":"A non-root/limited admin (or a user whose role strips sensitive-edit permission) opens the 'fetch models' preview in the channel drawer; the permission check fails before any API call is made.","commonSituations":"Logged in as a viewer/limited admin; permission changed server-side after the page loaded (stale UI); root delegated channel management but not key visibility.","solutions":["Hide or disable the fetch-models button when canEditSensitive is false instead of letting the fetcher throw","Grant the account sensitive-edit permission if it should manage channel keys/models","Re-login/refresh after permission changes so the UI re-evaluates canEditSensitive","If you are root and still see this, check that the permission computation (role flags) is intact"],"exampleFix":"// before\n<Button onClick={() => setFetchModelsDialogOpen(true)}>Fetch models</Button>\n// after\n<Button disabled={!canEditSensitive} onClick={() => setFetchModelsDialogOpen(true)}>\n  Fetch models\n</Button>","handlingStrategy":"validation","validationCode":"if (!canEditSensitive) return // hide/disable the fetch-models action entirely","typeGuard":"const canFetchModelList = (\n  canEditSensitive: boolean,\n  isEditing: boolean,\n  type: number,\n  channelId: number | null\n): boolean =>\n  canEditSensitive && !(isEditing && type === CHANNEL_TYPE_ADVANCED_CUSTOM && channelId == null)","tryCatchPattern":null,"preventionTips":["Derive button visibility from canEditSensitive instead of throwing in the fetcher","Recompute permissions on session refresh so stale flags do not survive role changes","Keep the fetcher's throw as defense in depth, not the primary gate"],"tags":["permissions","rbac","channels","fetch-models","guard"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}