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
- Read the wrapped cause after 'download failed:' to identify the transport issue and fix connectivity accordingly
- Verify network access and DNS for the source host (e.g. curl the download URL from the same machine)
- Configure proxy environment variables (HTTP_PROXY/HTTPS_PROXY) if the host is only reachable via a proxy
- 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
- Ensure the deployment has outbound HTTPS access (allowlist codeload.github.com, raw.githubusercontent.com)
- Configure HTTP_PROXY/HTTPS_PROXY where direct egress is blocked
- Distinguish transport errors from HTTP-status errors before deciding to retry
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
- download custom emoji failed
- download custom emoji failed with status
- download failed:
- download failed: HTTP
- download generated image failed with status
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)