{"record":{"id":"ee97c8dc6c902620","repo":"siyuan-note/siyuan","slug":"download-failed","errorCode":null,"errorMessage":"download failed: ","messagePattern":"download failed: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill.go","lineNumber":699,"sourceCode":"\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\n\tcontentType = resp.Header.Get(\"Content-Type\")\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxSkillDownloadBytes+1))\n\tif err != nil {\n\t\treturn nil, \"\", errors.New(\"read body failed: \" + err.Error())\n\t}\n\tif len(body) > maxSkillDownloadBytes {\n\t\treturn nil, \"\", errors.New(\"skill source too large (limit 10MB)\")\n\t}\n\treturn body, contentType, nil\n}\n","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/skill.go#L681-L717","documentation":"fetchBytes performs the actual GET with the shared httpclient; if the HTTP transport itself fails (connection error, DNS failure, TLS error, timeout), the underlying error text is wrapped in a fixed `download failed: ` prefix (note: no HTTP status here — status failures produce `download failed: HTTP <code>` instead).","triggerScenarios":"Any InstallSkill download whose underlying http.Get errors: DNS resolution failure, connection refused/reset, TLS handshake failure, proxy misconfiguration, or request timeout — on codeload.github.com, raw.githubusercontent.com, or any custom direct URL.","commonSituations":"Offline or firewalled environments blocking github domains; corporate proxies without configured trust; DNS hijacking; IPv6 issues; transient network flaps during skill installation.","solutions":["Check network connectivity to the host in the URL (curl the download URL from the same machine)","Fix proxy/TLS configuration for the environment (proxy env vars, trusted CA certs)","Retry after transient failures; the error text (after the prefix) names the root cause","Use an alternative source URL (mirror, self-hosted zip) reachable from the network"],"exampleFix":"// before\nutil.InstallSkill(\"https://internal.example.com/skill.zip\") // connection refused on restricted network\n// after\nutil.InstallSkill(\"https://codeload.github.com/owner/repo/zip/refs/heads/main\") // reachable host","handlingStrategy":"retry","validationCode":"// probe reachability before install\nresp, err := http.Get(\"https://codeload.github.com\")\nif err != nil {\n    return fmt.Errorf(\"network unreachable: %w\", err)\n}\nresp.Body.Close()","typeGuard":null,"tryCatchPattern":"if _, err := util.InstallSkill(src); err != nil {\n    if strings.HasPrefix(err.Error(), \"download failed: \") && !strings.Contains(err.Error(), \"HTTP \") {\n        // transport-level failure: retry with backoff, then report connectivity problem\n    }\n    return err\n}","preventionTips":["Check network/proxy reachability to github.com domains before bulk installs","Configure proxy env vars and trusted CAs for corporate/firewalled environments","Implement bounded retries with backoff for transient transport errors","Read the text after the `download failed: ` prefix — it names the underlying DNS/TLS/connection cause"],"tags":["go","network","http","download","timeout"],"backgroundTag":"network-request-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}