siyuan-note/siyuan · error

read form part [%s] file [%s] error: %s

Error message

read form part [%s] file [%s] error: %s

What it means

Returned by the plugin server's multipart parser after handler.Open() succeeded but file.Read(content) failed while reading an uploaded file's bytes into a buffer of handler.Size. Identifies part name and filename; aborts the whole form parse.

Source

Thrown at kernel/plugin/server.go:334

		for partName, fileHandlers := range multipartForm.File {
			files := make([]*RequestFile, len(fileHandlers))
			form.File[partName] = files
			for i, handler := range fileHandlers {
				files[i] = &RequestFile{
					Filename: handler.Filename,
					Headers:  handler.Header,
					Size:     handler.Size,
				}
				file, openErr := handler.Open()
				if openErr != nil {
					err = fmt.Errorf("open form part [%s] file [%s] error: %s", partName, handler.Filename, openErr.Error())
					return
				}
				content := make([]byte, handler.Size)
				n, readErr := file.Read(content)
				file.Close()
				if readErr != nil {
					err = fmt.Errorf("read form part [%s] file [%s] error: %s", partName, handler.Filename, readErr.Error())
					return
				}
				fileData := content[:n]
				files[i].Data = &fileData
			}
		}
	} else if len(c.Request.PostForm) > 0 {
		// application/x-www-form-urlencoded
		form = &RequestForm{
			Value: c.Request.PostForm,
			File:  nil,
		}
	}

	if form == nil {
		// Not a form request, read raw body data
		if rawData, readErr := c.GetRawData(); readErr != nil {
			// request don't have body, do nothing

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Handle partial reads: use io.ReadAll or loop file.Read until EOF rather than a single Read of handler.Size.
  2. Validate Content-Length / declared size against actual bytes before allocating.
  3. If client disconnects are common, return a clear 4xx and let the client retry.

Example fix

// before
content := make([]byte, handler.Size)
n, readErr := file.Read(content)
// after
content, readErr := io.ReadAll(io.LimitReader(file, maxUploadBytes))
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate declared size before allocating the read buffer.
function validateUploadSize(declared: number, max: number): void {
  if (declared <= 0) throw new Error('invalid declared size')
  if (declared > max) throw new Error('upload exceeds max size')
}

Try / catch

// Robust read: loop or io.ReadAll, tolerate short reads and EOF.
buf := make([]byte, 0, handler.Size)
tmp := make([]byte, 32*1024)
for {
  n, err := file.Read(tmp)
  buf = append(buf, tmp[:n]...)
  if err == io.EOF { break }
  if err != nil { return err }
}

Prevention

When it happens

Trigger: Reading the opened multipart file part fails — short read, stream corruption, client disconnect mid-upload, or handler.Size mismatched actual bytes. content is allocated as make([]byte, handler.Size); a read error before that many bytes triggers it.

Common situations: Client disconnects during upload; Content-Length lies about size; network reset; the multipart part is larger/smaller than declared and io.Reader returns an error; very large file exhausting buffers.

Related errors


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