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
- Provide at least one source: attach a 'file' part OR send a non-empty 'url' form field.
- If using URL mode, POST as form-urlencoded/multipart with url=<http(s) address>.
- 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
- Disable submit until either a file is selected or a URL entered.
- Use the exact field names 'file' / 'url'.
- POST as multipart/form-data or form-urlencoded, not JSON.
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
- no file found
- invalid custom emoji URL
- asset data is empty
- invalid asset filename
- task list item marker length should be 1
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/52cd0e6cdf332a90.
Report an issue: GitHub.