{"record":{"id":"0f64d19e9c9dd11c","repo":"siyuan-note/siyuan","slug":"download-failed-tried-main-and-master-v","errorCode":null,"errorMessage":"download failed (tried main and master): %v","messagePattern":"download failed \\(tried main and master\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill.go","lineNumber":694,"sourceCode":"\t\treturn nil, \"\", fmt.Errorf(\"invalid download URL: %s\", src.downloadURL)\n\t}\n\tif cerr := CheckHostSSRF(u.Hostname()); cerr != nil {\n\t\treturn nil, \"\", cerr\n\t}\n\n\tdata, contentType, err = fetchBytes(src.downloadURL)\n\tif err == nil {\n\t\treturn data, contentType, nil\n\t}\n\n\t// codeload main 分支 404 时回退 master\n\tif src.isZip && src.branch == \"main\" {\n\t\townerRepo := strings.TrimPrefix(strings.TrimPrefix(src.downloadURL, \"https://codeload.github.com/\"), \"http://codeload.github.com/\")\n\t\townerRepo = strings.TrimSuffix(ownerRepo, \"/zip/refs/heads/main\")\n\t\tfallback := codeloadSource(ownerRepo, \"master\")\n\t\tdata, contentType, ferr := fetchBytes(fallback.downloadURL)\n\t\tif ferr != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"download failed (tried main and master): %v\", err)\n\t\t}\n\t\treturn data, contentType, nil\n\t}\n\treturn nil, \"\", err\n}\n\n// fetchBytes 执行带大小限制的 GET\nfunc fetchBytes(rawURL string) (data []byte, contentType string, err error) {\n\tresp, err := httpclient.NewBrowserRequest().Get(rawURL)\n\tif err != nil {\n\t\treturn nil, \"\", errors.New(\"download failed: \" + err.Error())\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode >= 400 {\n\t\treturn nil, \"\", fmt.Errorf(\"download failed: HTTP %d\", resp.StatusCode)\n\t}\n","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill.go#L676-L712","documentation":"When downloading a GitHub repo zip, the library tries the 'main' branch on codeload and, if that fails, retries with 'master'. If both branches fail to download, it reports 'download failed (tried main and master)' with the original (main) error. It means the repo is unreachable under either default branch name.","triggerScenarios":"InstallSkill with a github.com owner/repo (or tree URL) where codeload zip download for refs/heads/main and refs/heads/master both fail — typically both return HTTP 404 because the default branch has another name, or the repo does not exist / is private.","commonSituations":"Repo uses a non-standard default branch (e.g. 'develop', 'trunk'); repo renamed or deleted; private repo requiring auth; network outage to codeload.github.com; typo in owner/repo.","solutions":["Check the repo's actual default branch on GitHub and pass an explicit tree URL: https://github.com/owner/repo/tree/<branch>","Verify the repo exists and is public (private repos cannot be fetched anonymously)","Fix any typo in owner/repo in the source URL","Check network/proxy access to codeload.github.com; the wrapped error from the main-branch attempt shows the root cause (HTTP 404 vs connection error)"],"exampleFix":"// before\nInstallSkill(\"https://github.com/owner/repo\") // default branch is 'develop'\n// after\nInstallSkill(\"https://github.com/owner/repo/tree/develop\")","handlingStrategy":"fallback","validationCode":"// Proactively check which default branch exists before installing\nconst branches = [\"main\", \"master\", \"develop\"]\nfor (const b of branches) {\n  const r = await fetch(`https://api.github.com/repos/${owner}/${repo}/branches/${b}`)\n  if (r.ok) { useTreeUrl(`https://github.com/${owner}/${repo}/tree/${b}`); break }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await installSkill(repoUrl)\n} catch (e) {\n  if (String(e).includes(\"tried main and master\")) {\n    // surface repo-existence / branch-name guidance to the user\n    showHint(\"Verify the repo exists, is public, and check its default branch name\")\n  }\n}","preventionTips":["Prefer explicit /tree/<branch> URLs over bare repo URLs for non-main default branches","Verify the repository is public before installing anonymously","Check codeload.github.com reachability when installs fail wholesale"],"tags":["network","github","download","branch"],"backgroundTag":"http-error-response","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}