siyuan-note/siyuan · error

invalid custom emoji URL

Error message

invalid custom emoji URL

What it means

downloadCustomEmojiData parses the supplied emoji URL and requires an http or https scheme plus a non-empty host. Anything else (ftp:, data:, missing scheme, bare hostname) is rejected as "invalid custom emoji URL" before any network request is made.

Solutions

  1. Prefix the URL with https:// (or http://) if the scheme is missing
  2. Use only http/https URLs that include a host
  3. Trim whitespace and fix typos in the scheme
  4. Validate with new URL(rawURL) in JS or url.Parse in Go before calling the API

Example fix

// before
addEmoji({ url: "www.example.com/emoji.png" }) // invalid custom emoji URL
// after
addEmoji({ url: "https://www.example.com/emoji.png" })
Defensive patterns

Strategy: validation

Validate before calling

function isHttpUrl(s) {
  try { const u = new URL(s.trim()); return u.protocol === "http:" || u.protocol === "https:"; } catch { return false; }
}
if (!isHttpUrl(rawUrl)) alert("Please enter a full http(s) URL");

Type guard

function isHttpUrl(s) {
  try { const u = new URL(s.trim()); return u.protocol === "http:" || u.protocol === "https:"; } catch { return false; }
}

Prevention

When it happens

Trigger: Passing a URL with a scheme other than http/https, a scheme-less string like "example.com/a.png", a malformed URL that url.Parse cannot handle, or a URL without a host.

Common situations: Users pasting "www.example.com/img.png" without the scheme; internal file:// or data: URIs; typos like "htp://"; URLs with stray whitespace or full-width characters.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at kernel/api/system.go:339

		file, err := fileHeader.Open()
		if err != nil {
			return nil, err
		}
		defer file.Close()
		return io.ReadAll(io.LimitReader(file, maxCustomEmojiSize+1))
	}

	rawURL := strings.TrimSpace(request.URL)
	if rawURL == "" {
		return nil, fmt.Errorf("field [file] or [url] must not be empty")
	}
	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)
	}

View on GitHub (pinned to 9f775e8a12)