{"record":{"id":"dcfb3a485a21a623","repo":"Significant-Gravitas/AutoGPT","slug":"failed-to-download-skill-http-res-status","errorCode":null,"errorMessage":"Failed to download skill (HTTP ${res.status})","messagePattern":"Failed to download skill \\(HTTP (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"autogpt_platform/frontend/src/components/contextual/SkillsPanel/components/SkillListItem/useSkillListItem.ts","lineNumber":80,"sourceCode":"\n  function closeDelete(open: boolean) {\n    setIsDeleteOpen(open);\n  }\n\n  function openView() {\n    setIsViewOpen(true);\n  }\n\n  function closeView(open: boolean) {\n    setIsViewOpen(open);\n  }\n\n  async function handleDownload() {\n    setIsDownloading(true);\n    try {\n      const res = await readCopilotSkill(skill.name);\n      if (res.status !== 200) {\n        throw new Error(`Failed to download skill (HTTP ${res.status})`);\n      }\n      const skillDetail = res.data as CopilotSkillDetail;\n      downloadTextFile(`${skill.name}.md`, renderSkillMarkdown(skillDetail));\n    } catch (error) {\n      toast({\n        title: \"Failed to download skill\",\n        description:\n          error instanceof Error\n            ? error.message\n            : \"An unexpected error occurred.\",\n        variant: \"destructive\",\n      });\n    } finally {\n      setIsDownloading(false);\n    }\n  }\n\n  async function handleDelete() {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/frontend/src/components/contextual/SkillsPanel/components/SkillListItem/useSkillListItem.ts#L62-L98","documentation":"Thrown in SkillListItem's download handler when readCopilotSkill(skill.name) resolves with a status other than 200. The generated client resolves rather than throws on non-2xx, so this explicit check is the only failure surface. The HTTP code is embedded in the message and the error is shown in a destructive toast.","triggerScenarios":"GET copilot skill detail by name returning 404 (skill renamed/removed from the catalog between panel load and click), 401/403 (session expired while the panel was open), or 5xx (backend down).","commonSituations":"Skills list loaded, backend upgraded/skill removed, then user clicks download; long-lived tab with expired auth; backend skill directory out of sync with the frontend catalog.","solutions":["Read the embedded HTTP code: 404 → refresh the skills panel (list is stale); 401 → sign in again; 5xx → retry when backend is healthy.","Refresh the SkillsPanel list before retrying so stale names are dropped.","If it persists for a skill that shows in the list, the backend skill registry and the listing API disagree — report the skill name."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isSkillDownloadFailure(err: unknown): boolean {\n  return err instanceof Error && err.message.startsWith(\"Failed to download skill\");\n}","tryCatchPattern":"try {\n  const res = await readCopilotSkill(skill.name);\n  if (res.status !== 200) throw new Error(`Failed to download skill (HTTP ${res.status})`);\n} catch (error) {\n  toast({ title: \"Failed to download skill\", description: (error as Error).message, variant: \"destructive\" });\n}","preventionTips":["Always check .status on generated-client responses — they resolve instead of throwing on non-2xx.","Refetch the skills list when a 404 appears instead of letting the user retry a stale name.","Disable the download button while a download is in flight (setIsDownloading already guards double-clicks)."],"tags":["skills","download","http-status","copilot","frontend"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}