OpenNHP/opennhp · error
could not copy file to server
Error message
could not copy file to server: %v
What it means
After creating the form part, the file is streamed into the multipart body with io.Copy(part, progressReader). The error 'could not copy file to server: %v' wraps an io.Copy failure — typically a read error from the source file (I/O error, file removed/truncated mid-read) or a write error into the in-memory body (allocation failure). Despite the wording, at this stage nothing has been sent to the server; the copy is into a local buffer.
Solutions
- Check the wrapped error: read errors indicate source file/disk problems, write/allocation errors indicate memory exhaustion
- For very large files, ensure the host has RAM roughly equal to the file size, since body is an in-memory bytes.Buffer
- Verify the file is not being modified or truncated during upload
- Check disk health (dmesg / smartctl) for I/O errors on the source volume
- Consider chunked or streaming uploads (io.Pipe into http.NewRequest) for large files
Example fix
// before
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// after
// stream large files instead of buffering entirely in memory
pr, pw := io.Pipe()
writer := multipart.NewWriter(pw)
go func() {
defer pw.Close()
part, _ := writer.CreateFormFile("file", filepath.Base(filePath))
io.Copy(part, progressReader)
writer.Close()
}()
req, _ := http.NewRequest("POST", uploadUrl, pr) Defensive patterns
Strategy: try-catch
Validate before calling
info, err := os.Stat(path)
if err != nil {
return err
}
if uint64(info.Size()) > maxInMemoryUploadBytes {
return fmt.Errorf("file too large for in-memory upload buffer (%d bytes)", info.Size())
} Type guard
func fitsInMemory(path string, limit int64) bool {
info, err := os.Stat(path)
return err == nil && info.Size() <= limit
} Try / catch
url, err := dev.UploadFileToNHPServer(filePath)
if err != nil {
if strings.Contains(err.Error(), "could not copy file to server") {
return fmt.Errorf("local copy into multipart body failed (read or OOM), no data was sent: %w", err)
}
return err
} Prevention
- Do not modify or truncate the file while the upload is in progress
- For large files, stream via io.Pipe into http.NewRequest instead of bytes.Buffer
- Monitor process memory; the whole body is buffered in RAM
- Check source disk health when read errors recur
When it happens
Trigger: Calling UploadFileToNHPServer when the source file experiences a read I/O error mid-copy (disk failure, file truncated concurrently, NFS hiccup), or the process runs out of memory growing bytes.Buffer for a very large file.
Common situations: Uploading multi-gigabyte ZTDO files into an in-memory buffer on a memory-limited host; disk errors on the source volume; another process truncating the file during upload; OOM killer pressure.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- could not create form file
- could not close writer
- could not get file info
- could not read response body
- failed to read file content
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/0dd3021060465f5e.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/utils.go:237
startTime := time.Now()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return "", fmt.Errorf("could not create form file: %v", err)
}
progressReader := &ProgressReader{
Reader: file,
Progress: progress,
}
_, err = io.Copy(part, progressReader)
if err != nil {
return "", fmt.Errorf("could not copy file to server: %v", err)
}
err = writer.Close()
if err != nil {
return "", fmt.Errorf("could not close writer: %v", err)
}
uploadUrl := httpHost + "storage/upload"
req, err := http.NewRequest("POST", uploadUrl, body)
if err != nil {
return "", fmt.Errorf("could not create request: %v", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{
Timeout: 120 * time.Minute,View on GitHub (pinned to 6e04ca5ff0)