siyuan-note/siyuan · error

invalid custom emoji image

Error message

invalid custom emoji image

What it means

Returned by normalizeCustomEmojiData for raster images (png/jpeg/gif/webp detected by http.DetectContentType) when image.DecodeConfig fails OR the dimensions are invalid: width or height < 1, either side > 16384 px, or width*height > 100,000,000 pixels. The bytes smelled like a raster image but are corrupt or absurdly large.

Source

Thrown at kernel/api/system.go:375

	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 251596fc0d)

Solutions

  1. Re-open the file in an image editor and re-export to confirm it is a valid image.
  2. Downscale so neither side exceeds 16384 px and total pixels stay under 100M.
  3. Verify the file with `file icon.png` and `identify icon.png` (ImageMagick) before uploading.
  4. If the file is valid but exotic (e.g. AVIF/HEIC), convert to png/webp first; those formats are not in the accepted set.

Example fix

// before
// uploading a truncated 20000x20000 png -> 'invalid custom emoji image'

// after: validate locally first
//   identify icon.png   # confirm dimensions are sane and body is complete
//   convert icon.png -resize 512x512 icon_small.png
// then upload icon_small.png
Defensive patterns

Strategy: validation

Validate before calling

// Validate raster dimensions locally before uploading
cfg, _, err := image.DecodeConfig(f)
if err != nil { return fmt.Errorf("not a decodable image: %w", err) }
if cfg.Width < 1 || cfg.Height < 1 { return errors.New("invalid dimensions") }
if cfg.Width > 16384 || cfg.Height > 16384 { return errors.New("dimension exceeds 16384px") }
if int64(cfg.Width)*int64(cfg.Height) > 100_000_000 { return errors.New("pixel count exceeds 100M") }

Prevention

When it happens

Trigger: Uploading a truncated PNG/GIF (headers intact, body cut off so DecodeConfig fails), a deliberately huge dimension image (>16384 px), or a file with a spoofed extension whose magic bytes match a raster type but the body is malformed.

Common situations: Partial download saved as an image; a screenshot exported at extreme resolution; a file renamed .png but actually a different binary; a JPEG with only SOI marker.

Related errors


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