siyuan-note/siyuan · error

download custom emoji failed: %w

Error message

download custom emoji failed: %w

What it means

Returned by downloadCustomEmojiData (system.go:336) when the HTTP GET to fetch the custom-emoji URL failed at the transport level (DNS, connection refused, TLS handshake, timeout, etc.). The underlying error is wrapped with %w so the cause is preserved; the response may not have been received at all, so no status code is reported.

Source

Thrown at kernel/api/system.go:336

		return io.ReadAll(io.LimitReader(file, maxCustomEmojiSize+1))
	}

	rawURL := strings.TrimSpace(c.PostForm("url"))
	if rawURL == "" {
		return nil, fmt.Errorf("field [file] or [url] must not be empty")
	}
	return downloadCustomEmojiData(rawURL)
}

func downloadCustomEmojiData(rawURL string) ([]byte, error) {
	parsedURL, err := url.Parse(rawURL)
	if err != nil || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") || parsedURL.Host == "" {
		return nil, fmt.Errorf("invalid custom emoji URL")
	}

	response, err := util.NewCustomReqClient().R().Get(parsedURL.String())
	if err != nil {
		return nil, fmt.Errorf("download custom emoji failed: %w", err)
	}
	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("download custom emoji failed with status %d", response.StatusCode)
	}
	if response.ContentLength > maxCustomEmojiSize {
		return nil, fmt.Errorf("custom emoji file is too large")
	}

	data, err := io.ReadAll(io.LimitReader(response.Body, maxCustomEmojiSize+1))
	if err != nil {
		return nil, fmt.Errorf("read custom emoji response failed: %w", err)
	}
	return data, nil
}

func normalizeCustomEmojiData(data []byte) (normalized []byte, ext string, err error) {
	if len(data) == 0 {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Verify the URL is reachable from the SiYuan host (curl -I <url>) and the TLS cert is valid.
  2. If the host is blocked by a corporate proxy, configure the kernel HTTP proxy settings and retry.
  3. Retry once for transient failures; if it persists, download the file and use the 'file' upload mode instead.
  4. Check the wrapped error (err.Unwrap / errors.Unwrap) for the exact transport cause (DNS, TLS, timeout).
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check reachability is not guaranteed; wrap in retry with backoff
async function downloadWithRetry(url, n=2) {
  for (let i=0;i<n;i++){ try { return await downloadEmoji(url); } catch(e){ if (/download custom emoji failed/.test(e.message) && i<n-1) await sleep(500*(i+1)); else throw e; } }
}

Try / catch

try { await uploadEmojiFromUrl(url); }
catch (e) {
  if (/download custom emoji failed/.test(e.msg)) { /* inspect e.cause: DNS/TLS/timeout; fix and retry, or fall back to file upload */ }
  else throw e;
}

Prevention

When it happens

Trigger: The emoji `url` points to a host that does not resolve, refuses the connection, has an expired/broken TLS certificate, or the request exceeded the custom-request-client timeout. util.NewCustomReqClient().R().Get(parsedURL.String()) at system.go:334 returns a non-nil err and it is wrapped at line 336.

Common situations: Offline or behind a firewall/proxy that blocks the emoji host. Stale/expired CDN link. Host requires auth or blocks the SiYuan user-agent. Transient network blip. Self-signed or outdated CA store causing TLS failure.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/1fd3ea8eb9a0ff8d. Report an issue: GitHub.