siyuan-note/siyuan · error

invalid custom emoji name

Error message

invalid custom emoji name

What it means

First throw of 'invalid custom emoji name' in normalizeCustomEmojiPath's loop. After TrimSpace, a path segment is empty, '.', or '..'. This catches empty segments (leading/trailing/double slash), current-directory, and parent-directory references — i.e. path traversal or malformed names supplied via the name form field.

Source

Thrown at kernel/api/system.go:402

	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")
		}
		part = util.FilterUploadFileName(part)
		if part == "" || part == "." || part == ".." {
			return "", fmt.Errorf("invalid custom emoji name")
		}
		parts[i] = part
	}
	parts[lastIndex] += ext
	return strings.Join(parts, "/"), nil
}

func checkUpdate(c *gin.Context) {
	ret := gulu.Ret.NewResult()
	defer c.JSON(http.StatusOK, ret)

	arg, ok := util.JsonArg(c, ret)
	if !ok {
		return

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Provide a simple non-empty basename without slashes, e.g. "myemoji" or "group/icon".
  2. Strip leading/trailing slashes and collapse repeated slashes before submitting.
  3. Never send '.' or '..' segments; they are rejected to prevent escaping the emojis directory.

Example fix

// before
c.PostForm("name") == "../etc/passwd"   // -> invalid custom emoji name

// after: submit a flat, sanitized name
name := "myemoji"   // or "packs/happy" for a subfolder
// client side:
//   name = name.trim().replace(/\/+/g, '/').replace(/^\/+|\/+$/g, '')
Defensive patterns

Strategy: validation

Validate before calling

// Reject path-traversal / empty segments before submitting the name
name = strings.TrimSpace(strings.ReplaceAll(name, "\\", "/"))
for _, part := range strings.Split(name, "/") {
    part = strings.TrimSpace(part)
    if part == "" || part == "." || part == ".." {
        return errors.New("name contains an empty or traversal segment")
    }
}

Prevention

When it happens

Trigger: POST /api/system/addCustomEmoji with name like "", "/icon", "a//b", "../icon", "./x", or "." — any segment that is empty or a dot/dotdot after trimming. A trailing slash or a path-traversal attempt lands here.

Common situations: Client sends name with a trailing slash; user types a relative path hoping to write outside the emojis dir; front-end bug submits an empty name when the field is left blank.

Related errors


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