siyuan-note/siyuan · error

read custom emoji response failed

Error message

read custom emoji response failed: %w

What it means

Reading the HTTP response body into memory failed while downloading a custom emoji. The read is performed with io.ReadAll over a LimitReader capped at maxCustomEmojiSize+1, so only genuine I/O errors (connection reset, truncated body, timeouts) reach this wrapper.

Solutions

  1. Retry the download; transient truncation usually clears
  2. Check proxy/firewall stability for the remote host
  3. Use a more reliable mirror/CDN URL for the emoji
  4. Verify TLS/connectivity to the host (curl the URL to reproduce)

Example fix

// before
data, _ := io.ReadAll(body) // may fail mid-stream
// after
data, err := io.ReadAll(io.LimitReader(body, maxCustomEmojiSize+1))
if err != nil {
    return retryDownload(url) // retry transient truncation
}
Defensive patterns

Strategy: retry

Try / catch

try {
  return await downloadEmoji(url);
} catch (e) {
  if (String(e).includes("read custom emoji response failed")) {
    return retryWithBackoff(() => downloadEmoji(url), 3); // transient truncation
  }
  throw e;
}

Prevention

When it happens

Trigger: The connection drops or the server closes the stream mid-body during downloadCustomEmojiData; TLS interruption; read timeout while streaming the body.

Common situations: Flaky network or unstable proxy between the kernel and the emoji host; server aborts large responses; keep-alive reuse of a stale connection.

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/98f2019529b7ae5a. Report an issue: GitHub.

Appendix: source

Thrown at kernel/api/system.go:356

	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
	switch http.DetectContentType(data) {
	case "image/png":
		ext = ".png"
	case "image/jpeg":
		ext = ".jpg"
	case "image/gif":
		ext = ".gif"
	case "image/webp":

View on GitHub (pinned to 9f775e8a12)