siyuan-note/siyuan · error

custom emoji file is too large

Error message

custom emoji file is too large

What it means

Returned when the emoji payload exceeds maxCustomEmojiSize (10 MiB). Two throw sites: (1) downloadCustomEmojiData checks response.ContentLength > 10 MiB for the url path; (2) the addCustomEmoji handler checks len(data) > maxCustomEmojiSize for the uploaded file path. Note the url-path check only fires when the server advertises Content-Length; chunked responses with no length skip it and are instead capped by the LimitReader on line 348.

Source

Thrown at kernel/api/system.go:343

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

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Re-export the image at emoji-appropriate dimensions (e.g. 128x128 or 256x256) before uploading.
  2. Strip metadata and re-compress: pngquant/oxipng for PNG, cwebp for WebP, gifsicle with --lossy for GIF.
  3. If the emoji must be large, split it or accept the built-in 10 MiB cap cannot be raised without editing maxCustomEmojiSize and rebuilding.
  4. For the url path, confirm the host sets Content-Length so the size guard actually fires; otherwise the LimitReader silently truncates at 10 MiB+1 and yields a corrupt-image error downstream.

Example fix

// before
// user uploads a 24 MiB photo as an emoji -> 413 too large

// after: downscale and compress before upload
//   convert icon.png -resize 128x128 icon_128.png
//   oxipng -o3 icon_128.png   // typically < 100 KiB
// then POST /api/system/addCustomEmoji with file=@icon_128.png
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: check size before upload
const MAX = 10 * 1024 * 1024
fi, err := os.Stat(path)
if err != nil { return err }
if fi.Size() > MAX { return fmt.Errorf("file is %d bytes, max %d", fi.Size(), MAX) }

Prevention

When it happens

Trigger: Uploading an emoji file larger than 10 MiB via the file form field; or providing a url whose server reports Content-Length > 10 MiB. Animated GIFs, high-resolution PNGs, and oversized SVGs are the usual offenders.

Common situations: A full-resolution photo used as an emoji; a multi-megabyte animated GIF; a SVG exported with embedded base64 raster data inflating its size.

Related errors


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