siyuan-note/siyuan · error
no file found
Error message
no file found
What it means
saveImportUploadFile extracts the uploaded file from a multipart request and rejects the call when the *multipart.FileHeader is nil with 'no file found'. The API contract expects exactly one uploaded file (e.g. the .sy zip) under the agreed form field.
Solutions
- Send multipart/form-data with the file under the exact expected field name.
- With fetch: do NOT set Content-Type manually — new FormData with formData.append("file", fileBlob, name).
- With curl: curl -F "file=@notebook.sy" <endpoint>.
- On the server, validate file != nil and return 400 with this message so clients can fix the request.
Example fix
// before
await fetch("/api/import/syUpload", {method:"POST", body: JSON.stringify({path})})
// after
const fd = new FormData(); fd.append("file", fileBlob, "notebook.sy")
await fetch("/api/import/syUpload", {method:"POST", body: fd}) Defensive patterns
Strategy: validation
Validate before calling
if (!(blob instanceof Blob) || blob.size === 0) throw new Error("attach a non-empty file before upload")
const fd = new FormData(); fd.append("file", blob, "notebook.sy") Type guard
function hasFile(fd) { return fd.get("file") instanceof File && fd.get("file").size > 0 } Try / catch
try { await upload(fd) }
catch (e) { if (e.message === "no file found") notify("Select a .sy file to upload") else throw e } Prevention
- Always use FormData for this endpoint; never JSON bodies
- Use the exact expected form field name
- Let fetch set Content-Type automatically (multipart boundary)
When it happens
Trigger: POSTing to the .sy upload endpoint with no 'file' form field, an empty part, wrong field name, or a client that sends JSON instead of multipart/form-data.
Common situations: fetch with Content-Type application/json instead of FormData; curl missing -F file=@...; the form field name mistyped (e.g. 'files' vs 'file'); proxies stripping empty file parts.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/29ba78a3cd230a9d.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/import.go:284
func stagedSYImportPath(token string) string {
return filepath.Join(stagedSYImportDir(), token+".zip")
}
func isValidSYImportToken(token string) bool {
if len(token) != 32 {
return false
}
for _, char := range token {
if !(char >= 'a' && char <= 'z') && !(char >= 'A' && char <= 'Z') && !(char >= '0' && char <= '9') {
return false
}
}
return true
}
func saveImportUploadFile(c *gin.Context, file *multipart.FileHeader) (writePath string, cleanup func(), err error) {
if file == nil {
return "", nil, errors.New("no file found")
}
importDir := filepath.Join(util.TempDir, "import", gulu.Rand.String(7))
if err = os.MkdirAll(importDir, 0755); err != nil {
return
}
cleanup = func() { _ = os.RemoveAll(importDir) }
writePath = filepath.Join(importDir, filepath.Base(file.Filename))
if !gulu.File.IsSubPath(importDir, writePath) {
err = errors.New("import path is not sub path of import dir")
cleanup()
return
}
if err = c.SaveUploadedFile(file, writePath); err != nil {
cleanup()
}
returnView on GitHub (pinned to 9f775e8a12)