siyuan-note/siyuan · error

fetch failed

Error message

fetch failed: %s

What it means

After SSRF validation passes, WebFetch performs ssrfSafeClient.Get(rawURL); any transport-level failure (DNS resolution, TCP connect, TLS handshake, timeout) is wrapped into this error with the underlying net/http message appended. It means the kernel could not complete the HTTP request at all — no HTTP status was received.

Solutions

  1. Read the wrapped cause after 'fetch failed: ' and fix accordingly (DNS, timeout, TLS)
  2. Verify the host resolves and is reachable: curl -v the same URL from the kernel host
  3. Check whether the URL points at a private/loopback address the SSRF-safe client refuses
  4. Increase tolerance for slow sites by checking client timeouts; retry transient network failures

Example fix

// before
_, err := WebFetch(target, "markdown") // opaque failure

// after
_, err := WebFetch(target, "markdown")
if err != nil && strings.HasPrefix(err.Error(), "fetch failed: ") {
    log.Printf("transport error for %s: %v", target, err) // inspect wrapped cause
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check reachability cheaply
if net.ParseIP(host) != nil && isPrivateIP(host) {
    return errors.New("target is a private address; will be refused")
}

Try / catch

content, err := WebFetch(url, format)
if err != nil && strings.HasPrefix(err.Error(), "fetch failed: ") {
    // transport-level failure; inspect wrapped cause, retry with backoff for timeouts/resets
}

Prevention

When it happens

Trigger: Host does not resolve (DNS failure), connection refused/timed out, TLS certificate errors, proxy failures, or the SSRF-safe client's dial policy blocking the connection (private IP re-check at connect time).

Common situations: Fetching an intranet hostname from a machine without VPN access; expired or self-signed certificates; the target site is down; corporate proxy not configured; DNS blocked in sandboxed environments.

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/31b104f071aaed86. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/webfetch.go:55

	maxWebFetchChars     = 50000
)

func WebFetch(rawURL, format string) (string, error) {
	u, err := url.Parse(rawURL)
	if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
		return "", errors.New("URL must start with http:// or https://")
	}
	if u.Host == "" {
		return "", errors.New("URL has no host")
	}

	if err := CheckHostSSRF(u.Hostname()); err != nil {
		return "", err
	}

	resp, err := ssrfSafeClient.Get(rawURL)
	if err != nil {
		return "", errors.New("fetch failed: " + err.Error())
	}
	defer resp.Body.Close()

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

	contentType := resp.Header.Get("Content-Type")
	maxReadBytes := int64(maxWebFetchBytes)
	if !strings.HasPrefix(contentType, "text/html") && !strings.HasPrefix(contentType, "text/plain") {
		maxReadBytes = maxWebFetchFileBytes
	}
	if resp.ContentLength > maxReadBytes {
		return "", errors.New("response too large")
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxReadBytes))
	if err != nil {

View on GitHub (pinned to 9f775e8a12)