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

  1. Send multipart/form-data with the file under the exact expected field name.
  2. With fetch: do NOT set Content-Type manually — new FormData with formData.append("file", fileBlob, name).
  3. With curl: curl -F "file=@notebook.sy" <endpoint>.
  4. 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

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()
	}
	return

View on GitHub (pinned to 9f775e8a12)