siyuan-note/siyuan · error

invalid custom emoji image

Error message

invalid custom emoji image

What it means

For raster images (png/jpeg/gif/webp), normalizeCustomEmojiData decodes the image configuration via image.DecodeConfig and enforces sane dimensions: width/height >= 1, <= 16384, and width*height <= 100M pixels. If decoding fails or the dimensions are out of bounds, it fails with 'invalid custom emoji image'.

Solutions

  1. Verify the file is a valid image and opens in an image viewer
  2. Re-export/resize the image to reasonable dimensions (under 16384x16384 and 100M pixels)
  3. Re-download — the file may be truncated or corrupted
  4. Ensure the URL serves raw image bytes, not a webpage
Defensive patterns

Strategy: validation

Validate before calling

const img = new Image();
img.onload = () => {
  if (img.width < 1 || img.width > 16384 || img.height < 1 || img.height > 16384)
    throw new Error("emoji dimensions out of range");
};
img.src = url;

Try / catch

try {
  await registerEmoji(data);
} catch (e) {
  if (String(e).includes("invalid custom emoji image")) {
    notifyUser("File is not a decodable image or its dimensions are too extreme");
  }
}

Prevention

When it happens

Trigger: Downloading a file whose content is not a decodable raster image despite the extension (corrupted file, HTML error page), or an image with extreme dimensions (e.g. a 20000x20000 decompression-bomb PNG).

Common situations: URL serves an HTML login/error page with image extension; truncated download yields undecodable bytes; attacker-supplied huge-dimension image blocked as a decompression bomb.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at kernel/api/system.go:383

	raster := true
	switch http.DetectContentType(data) {
	case "image/png":
		ext = ".png"
	case "image/jpeg":
		ext = ".jpg"
	case "image/gif":
		ext = ".gif"
	case "image/webp":
		ext = ".webp"
	default:
		raster = false
	}
	if raster {
		config, _, decodeErr := image.DecodeConfig(bytes.NewReader(data))
		if decodeErr != nil || config.Width < 1 || config.Height < 1 || config.Width > 16384 || config.Height > 16384 ||
			int64(config.Width)*int64(config.Height) > 100*1000*1000 {
			return nil, "", fmt.Errorf("invalid custom emoji image")
		}
		return data, ext, nil
	}

	sanitizedSVG, sanitizeErr := util.SanitizeSVG(string(data))
	if sanitizeErr == nil {
		return []byte(sanitizedSVG), ".svg", nil
	}
	return nil, "", fmt.Errorf("unsupported custom emoji image format")
}

func normalizeCustomEmojiPath(name, ext string) (string, error) {
	name = strings.TrimSpace(strings.ReplaceAll(name, "\\", "/"))
	parts := strings.Split(name, "/")
	if len(parts) == 0 {
		return "", fmt.Errorf("custom emoji name must not be empty")
	}

View on GitHub (pinned to 9f775e8a12)