{"record":{"id":"b0094af13f542e33","repo":"QuantumNous/new-api","slug":"channel-is-not-selected","errorCode":null,"errorMessage":"Channel is not selected","messagePattern":"Channel is not selected","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx","lineNumber":1363,"sourceCode":"    } else {\n      form.setValue('key', result.deduplicatedText)\n      toast.success(\n        t(\n          'Removed {{removed}} duplicate key(s). Before: {{before}}, After: {{after}}',\n          {\n            removed: result.removedCount,\n            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]","sourceCodeStart":1345,"sourceCodeEnd":1381,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L1345-L1381","documentation":"Thrown by fetchChannelKey in the channel mutate drawer when channelId is falsy at call time. The channel key endpoint requires an existing channel id (view/edit key of a saved channel), so creating-mode (channelId null/undefined/0) cannot fetch a key and fails fast with this English literal (not i18n'd).","triggerScenarios":"The unlock/view key action is invoked while the drawer is in create mode (channelId not yet assigned), or the id prop was never passed/propagated to this drawer instance.","commonSituations":"Component race where the key button renders before the id resolves; drawer reused for create after editing (stale closure or unreset state); caller passes id=0/undefined instead of null and the falsy check still trips.","solutions":["Only render/enable the unlock-key control when channelId is a positive number (isEditing mode)","Pass the real channel id from the row into the drawer and confirm it is set before opening the key section","Reset drawer state (including channelId) when switching between create and edit modes","In create mode, have the user paste the key directly — there is nothing to fetch yet"],"exampleFix":"// before\n{isEditing && <Button onClick={() => fetchChannelKey()} />}\n// after\n{isEditing && channelId != null && (\n  <Button onClick={() => fetchChannelKey()} />\n)}","handlingStrategy":"validation","validationCode":"if (!channelId) return // never render/enable key actions in create mode\nconst hasChannelId = channelId != null && channelId > 0","typeGuard":"const hasSavedChannel = (id: number | string | null | undefined): id is number =>\n  typeof id === 'number' && id > 0","tryCatchPattern":null,"preventionTips":["Enable the unlock-key control only in edit mode with a positive channel id","Reset channelId state when the drawer closes or switches purpose","Treat this throw as an invariant violation — fix the caller, do not catch it"],"tags":["state-guard","channel-key","drawer","validation"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}