siyuan-note/siyuan · error

download failed (tried main and master)

Error message

download failed (tried main and master): %v

What it means

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.

Solutions

  1. Check the repo's actual default branch on GitHub and pass an explicit tree URL: https://github.com/owner/repo/tree/<branch>
  2. Verify the repo exists and is public (private repos cannot be fetched anonymously)
  3. Fix any typo in owner/repo in the source URL
  4. 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)

Example fix

// before
InstallSkill("https://github.com/owner/repo") // default branch is 'develop'
// after
InstallSkill("https://github.com/owner/repo/tree/develop")
Defensive patterns

Strategy: fallback

Validate before calling

// Proactively check which default branch exists before installing
const branches = ["main", "master", "develop"]
for (const b of branches) {
  const r = await fetch(`https://api.github.com/repos/${owner}/${repo}/branches/${b}`)
  if (r.ok) { useTreeUrl(`https://github.com/${owner}/${repo}/tree/${b}`); break }
}

Try / catch

try {
  await installSkill(repoUrl)
} catch (e) {
  if (String(e).includes("tried main and master")) {
    // surface repo-existence / branch-name guidance to the user
    showHint("Verify the repo exists, is public, and check its default branch name")
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/0f64d19e9c9dd11c. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/skill.go:694

		return nil, "", fmt.Errorf("invalid download URL: %s", src.downloadURL)
	}
	if cerr := CheckHostSSRF(u.Hostname()); cerr != nil {
		return nil, "", cerr
	}

	data, contentType, err = fetchBytes(src.downloadURL)
	if err == nil {
		return data, contentType, nil
	}

	// codeload main 分支 404 时回退 master
	if src.isZip && src.branch == "main" {
		ownerRepo := strings.TrimPrefix(strings.TrimPrefix(src.downloadURL, "https://codeload.github.com/"), "http://codeload.github.com/")
		ownerRepo = strings.TrimSuffix(ownerRepo, "/zip/refs/heads/main")
		fallback := codeloadSource(ownerRepo, "master")
		data, contentType, ferr := fetchBytes(fallback.downloadURL)
		if ferr != nil {
			return nil, "", fmt.Errorf("download failed (tried main and master): %v", err)
		}
		return data, contentType, nil
	}
	return nil, "", err
}

// fetchBytes 执行带大小限制的 GET
func fetchBytes(rawURL string) (data []byte, contentType string, err error) {
	resp, err := httpclient.NewBrowserRequest().Get(rawURL)
	if err != nil {
		return nil, "", errors.New("download failed: " + err.Error())
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, "", fmt.Errorf("download failed: HTTP %d", resp.StatusCode)
	}

View on GitHub (pinned to 9f775e8a12)