siyuan-note/siyuan · error

download failed

Error message

download failed: %s

What it means

fetchBytes wraps transport-level failures of the HTTP GET it performs to download the skill source. Any error returned by the HTTP client itself (DNS failure, TLS error, connection refused/timeout, invalid URL rejected at request time) is reported as 'download failed: <underlying error>'.

Solutions

  1. Read the wrapped cause after 'download failed:' to identify the transport issue and fix connectivity accordingly
  2. Verify network access and DNS for the source host (e.g. curl the download URL from the same machine)
  3. Configure proxy environment variables (HTTP_PROXY/HTTPS_PROXY) if the host is only reachable via a proxy
  4. Retry on transient errors; for persistent failures switch to a mirrored or alternative source URL
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check reachability before install
const r = await fetch(downloadUrl, { method: "HEAD" }).catch(() => null)
if (!r) throw new Error("host unreachable; check network/DNS/proxy")

Try / catch

try {
  await installSkill(src)
} catch (e) {
  if (String(e).startsWith("download failed:")) {
    await retryWithBackoff(() => installSkill(src), { retries: 3 })
  }
}

Prevention

When it happens

Trigger: downloadSkillSource calls fetchBytes and httpclient.NewBrowserRequest().Get(rawURL) returns a non-nil error — no HTTP response was received at all.

Common situations: No internet connection or offline environment; DNS resolution failure for codeload.github.com or the custom host; corporate proxy/firewall blocking the request; TLS certificate problems; request timeouts.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at kernel/util/skill.go:705

	// 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)
	}

	contentType = resp.Header.Get("Content-Type")
	body, err := io.ReadAll(io.LimitReader(resp.Body, maxSkillDownloadBytes+1))
	if err != nil {
		return nil, "", errors.New("read body failed: " + err.Error())
	}
	if len(body) > maxSkillDownloadBytes {
		return nil, "", errors.New("skill source too large (limit 10MB)")
	}
	return body, contentType, nil
}

View on GitHub (pinned to 9f775e8a12)