siyuan-note/siyuan · error

unsupported custom emoji image format

Error message

unsupported custom emoji image format

What it means

If the bytes are neither a recognized raster format (png/jpeg/gif/webp) nor a sanitizable SVG, normalizeCustomEmojiData gives up with 'unsupported custom emoji image format'. Only those image formats are accepted as custom emoji.

Solutions

  1. Convert the image to PNG (or WebP/JPEG/GIF) before using it as an emoji
  2. Remove scripts/unsafe elements from the SVG so sanitization succeeds
  3. Check the URL serves one of the supported formats (DetectContentType is content-based, extension is irrelevant)
  4. Verify the server is not returning an HTML page

Example fix

// before
host("logo.bmp")
// after
convertToPNG("logo.bmp", "logo.png") // PNG is always supported
host("logo.png")
Defensive patterns

Strategy: fallback

Validate before calling

const type = (await fetch(url)).headers.get("content-type");
const ok = ["image/png","image/jpeg","image/gif","image/webp","image/svg+xml"].includes(type);
if (!ok) throw new Error(`format ${type} unsupported; convert to PNG`);

Try / catch

try {
  await registerEmoji(data);
} catch (e) {
  if (String(e).includes("unsupported custom emoji image format")) {
    const png = await convertToPNG(data); // fallback conversion
    return registerEmoji(png);
  }
  throw e;
}

Prevention

When it happens

Trigger: Downloading a file whose content type is not image/png, image/jpeg, image/gif, image/webp, or valid SVG — e.g. BMP, TIFF, ICO, PDF, or text — or an SVG that fails SanitizeSVG.

Common situations: User pastes a URL to an .ico favicon or .bmp image; server returns HTML instead of an image; SVG containing script is rejected by the sanitizer.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at kernel/api/system.go:392

	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")
	}

	lastIndex := len(parts) - 1
	switch strings.ToLower(filepath.Ext(parts[lastIndex])) {
	case ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg":
		parts[lastIndex] = strings.TrimSuffix(parts[lastIndex], filepath.Ext(parts[lastIndex]))
	}
	for i, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" || part == "." || part == ".." {
			return "", fmt.Errorf("invalid custom emoji name")

View on GitHub (pinned to 9f775e8a12)