{"record":{"id":"82f38788e6064da3","repo":"QuantumNous/new-api","slug":"failed-to-fetch-channel-key","errorCode":null,"errorMessage":"Failed to fetch channel key","messagePattern":"Failed to fetch channel key","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx","lineNumber":1370,"sourceCode":"            before: result.beforeCount,\n            after: result.afterCount,\n          }\n        )\n      )\n    }\n  }\n\n  const fetchChannelKey = useCallback(\n    async (proofToken?: string) => {\n      if (!channelId) {\n        throw new Error('Channel is not selected')\n      }\n\n      setIsChannelKeyLoading(true)\n      try {\n        const res = await getChannelKey(channelId, proofToken)\n        if (!res.success) {\n          throw new Error(res.message || t('Failed to fetch channel key'))\n        }\n\n        const keyValue = res.data?.key ?? ''\n        setChannelKey(keyValue)\n        toast.success(t('Channel key unlocked'))\n        return res\n      } finally {\n        setIsChannelKeyLoading(false)\n      }\n    },\n    [channelId, t]\n  )\n\n  const handleRevealKey = useCallback(async () => {\n    if (!channelId) return\n\n    try {\n      await withVerification(fetchChannelKey, {","sourceCodeStart":1352,"sourceCodeEnd":1388,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L1352-L1388","documentation":"Thrown by fetchChannelKey in the channel drawer when getChannelKey(channelId, proofToken) resolves with success=false. The backend message (e.g. key locked, proof verification required) replaces this i18n fallback; on success the key is revealed and 'Channel key unlocked' is toasted.","triggerScenarios":"Requesting the channel key when the server requires a passkey/WebAuthn proof token that is missing/invalid, when the admin lacks permission to view keys, when the channel id does not exist, or when the session has expired.","commonSituations":"Key-view permission policy enabled and the account lacks it; proof token expired so a fresh verification flow is needed; channel deleted concurrently; 401 after idle session.","solutions":["Read the toasted message — res.message carries the precise backend reason (proof required, forbidden, not found)","If a proof token is required, complete the verification step first and pass the fresh proofToken to the call","Confirm the logged-in account has permission to view channel keys (admin/root policy)","Re-open the drawer from a valid channel row so channelId is current, and retry"],"exampleFix":"// before\nconst res = await getChannelKey(channelId, proofToken)\nif (!res.success) {\n  throw new Error(res.message || t('Failed to fetch channel key'))\n}\n// after - branch on proof-required instead of a generic failure\nconst res = await getChannelKey(channelId, proofToken)\nif (!res.success) {\n  if (res.message?.toLowerCase().includes('proof')) {\n    openProofVerification()\n    return\n  }\n  throw new Error(res.message || t('Failed to fetch channel key'))\n}","handlingStrategy":"try-catch","validationCode":"if (!channelId) throw new Error('Channel is not selected') // keep guard\n// ensure a fresh proof token when the backend requires verification","typeGuard":null,"tryCatchPattern":"setIsChannelKeyLoading(true)\ntry {\n  const res = await getChannelKey(channelId, proofToken)\n  if (!res.success) throw new Error(res.message || t('Failed to fetch channel key'))\n  setChannelKey(res.data?.key ?? '')\n  return res\n} catch (error) {\n  toast.error(error instanceof Error ? error.message : t('Failed to fetch channel key'))\n  throw error // let proof-verification flows react\n} finally {\n  setIsChannelKeyLoading(false)\n}","preventionTips":["Check res.message for 'proof'/'verification' to route into the passkey flow instead of failing","Never cache proof tokens — request a fresh one per unlock","Verify the account has key-view permission before showing the unlock button"],"tags":["api-error","channel-key","auth","drawer","i18n"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}