OpenNHP/opennhp · error
could not close writer
Error message
could not close writer: %v
What it means
UploadFileToNHPServer builds a multipart/form-data body in memory for uploading a file to the NHP-DB server. After all parts are copied via io.Copy, it calls writer.Close() (multipart.Writer.Close), which writes the closing MIME boundary. If that fails, the multipart payload is truncated/incomplete and the function returns "could not close writer: %v" with the underlying cause.
Solutions
- Check available memory; the body is buffered entirely in RAM, so for large files switch to os.CreateTemp plus multipart.NewWriter(io.Writer) so Close() writes to disk instead of memory
- Read the wrapped error %v: if it is bytes.ErrTooLarge or an allocation failure, reduce file size or stream the upload
- Verify the file was fully copied before Close by checking the error (and progress.TotalSize) from the preceding io.Copy call
- Retry the upload after freeing memory; the failure is environmental, not a code bug in most cases
Example fix
// before
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
...
err = writer.Close()
if err != nil {
return "", fmt.Errorf("could not close writer: %v", err)
}
// after
tmpFile, err := os.CreateTemp("", "nhp-upload-*")
if err != nil {
return "", fmt.Errorf("could not create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
writer := multipart.NewWriter(tmpFile)
...
err = writer.Close()
if err != nil {
return "", fmt.Errorf("could not close writer: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
fi, err := os.Stat(filePath)
if err != nil { return err }
const maxInMemory = 512 << 20 // 512MB
if fi.Size() > maxInMemory {
return fmt.Errorf("file %s too large (%d bytes) for in-memory multipart upload", filePath, fi.Size())
} Type guard
func canBufferInMemory(size int64, limit int64) bool {
return size > 0 && size <= limit
} Try / catch
result, err := device.UploadFileToNHPServer(filePath)
if err != nil {
var memErr *errors.errorString
if strings.Contains(err.Error(), "could not close writer") || strings.Contains(err.Error(), "bytes.ErrTooLarge") {
// free memory / use disk-backed upload path, then retry
}
return fmt.Errorf("upload failed: %w", err)
} Prevention
- Stream large uploads through a temp file or pipe instead of bytes.Buffer to avoid memory exhaustion at Close()
- Monitor container memory limits (cgroup) before large uploads
- Always check the io.Copy error before Close; a partial copy makes the payload invalid anyway
- Test the upload path with the largest expected file size in CI
When it happens
Trigger: multipart.Writer.Close() returns an error inside UploadFileToNHPServer (endpoints/db/utils.go:240-243) after io.Copy(part, progressReader) succeeded. Practically this happens when the underlying bytes.Buffer write fails (e.g. the buffer cannot grow due to memory exhaustion for very large files) or a part writer was used incorrectly before closing.
Common situations: Uploading an extremely large file that exhausts memory while the in-memory body buffer grows; running in a memory-capped container (OOM pressure) so the final boundary write fails; less commonly, custom part writers misused before Close.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- could not create form file
- could not copy file to server
- unknown remote provider
- unknown remote provider
- unsupported key type, expect RSA
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/f3a497be339642b2.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/utils.go:242
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,
}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("could not send https request: %v", err)View on GitHub (pinned to 6e04ca5ff0)