siyuan-note/siyuan · error

field [file] or [url] must not be empty

Error message

field [file] or [url] must not be empty

What it means

Returned by readCustomEmojiData (system.go:323) when neither a multipart 'file' part nor a 'url' POST form field was supplied to the custom-emoji upload endpoint. The handler first tries c.FormFile("file"); if that errors, it falls back to the 'url' form field; if that is also empty/whitespace, the request is rejected because there is nothing to import.

Source

Thrown at kernel/api/system.go:323

	relativePath, _ = filepath.Rel(emojisDir, emojiPath)
	relativePath = filepath.ToSlash(relativePath)
	ret.Data = map[string]any{"path": relativePath}
}

func readCustomEmojiData(c *gin.Context) ([]byte, error) {
	fileHeader, fileErr := c.FormFile("file")
	if fileErr == nil {
		file, err := fileHeader.Open()
		if err != nil {
			return nil, err
		}
		defer file.Close()
		return io.ReadAll(io.LimitReader(file, maxCustomEmojiSize+1))
	}

	rawURL := strings.TrimSpace(c.PostForm("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)
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Provide at least one source: attach a 'file' part OR send a non-empty 'url' form field.
  2. If using URL mode, POST as form-urlencoded/multipart with url=<http(s) address>.
  3. Disable the submit control until one of the two inputs is non-empty.

Example fix

// before
fetchPost('/api/system/customEmoji', {})
// after
const fd = new FormData(); fd.append('url', 'https://cdn/emoji.png')
fetch('/api/system/customEmoji', { method:'POST', body: fd })
Defensive patterns

Strategy: validation

Validate before calling

// Require either a file blob or a non-empty URL
if (!fileBlob && !url) { throw new Error('provide a file or url'); }
const fd = new FormData();
if (fileBlob) fd.append('file', fileBlob); else fd.append('url', url);

Prevention

When it happens

Trigger: Calling the custom-emoji endpoint with a multipart form missing both the 'file' part and a 'url' field, or a JSON body without a url and no file attachment. readCustomEmojiData at system.go:310 falls through both branches to the empty-URL guard at line 322.

Common situations: Frontend lets the user choose upload-or-URL but submits before either is filled. Curl invocation missing both -F 'file=@...' and -F 'url=http://...'. Form field named 'link'/'src' instead of 'url'. Content-Type set to JSON with {url: ''}.

Related errors


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