siyuan-note/siyuan · error

download custom emoji failed with status

Error message

download custom emoji failed with status %d

What it means

downloadCustomEmojiData fetches a custom emoji image over HTTP and requires a 200 OK response. Any other status code (404, 403, 500, redirects that end in error, etc.) aborts the download with this error. It protects callers from writing error pages or empty responses to disk as emoji files.

Solutions

  1. Verify the URL is reachable and returns 200 (curl -I the URL)
  2. Check whether the remote host requires auth or blocks hotlinking; use a URL that serves the raw image
  3. If behind a proxy, fix proxy/auth settings so the request succeeds
  4. Retry later if the status is 429 or 5xx from a transient upstream

Example fix

// before
resp := util.NewCustomReqClient().R().Get(url)
// after (check status before consuming)
if resp.StatusCode() != 200 {
    return fmt.Errorf("emoji URL returned %d; use a direct image URL", resp.StatusCode())
}
Defensive patterns

Strategy: validation

Validate before calling

const resp = await fetch(url, {method: "HEAD"});
if (!resp.ok) throw new Error(`emoji URL returned ${resp.status}; use a direct image URL`);

Try / catch

try {
  const data = await downloadEmoji(url);
} catch (e) {
  if (String(e).includes("failed with status")) {
    notifyUser("The emoji URL is unreachable or returned an error page; check the URL");
  }
}

Prevention

When it happens

Trigger: The URL passed to the custom emoji download API (readCustomEmojiData flow) resolves to a server that answers with a non-200 HTTP status: missing file on the remote host, expired/hotlink-protected CDN link, or a proxy returning 403/502.

Common situations: Emoji URL points to a deleted GitHub repo path or renamed branch; corporate proxy blocks the request; rate-limited CDN returns 429; user pasted a webpage URL instead of a direct image URL.

Related errors


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

Appendix: source

Thrown at kernel/api/system.go:348

	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 {
		return nil, "", fmt.Errorf("custom emoji file must not be empty")
	}

	raster := true

View on GitHub (pinned to 9f775e8a12)