{"record":{"id":"f3a497be339642b2","repo":"OpenNHP/opennhp","slug":"could-not-close-writer-v","errorCode":null,"errorMessage":"could not close writer: %v","messagePattern":"could not close writer: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/db/utils.go","lineNumber":242,"sourceCode":"\n\tpart, err := writer.CreateFormFile(\"file\", filepath.Base(filePath))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not create form file: %v\", err)\n\t}\n\n\tprogressReader := &ProgressReader{\n\t\tReader:   file,\n\t\tProgress: progress,\n\t}\n\n\t_, err = io.Copy(part, progressReader)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not copy file to server: %v\", err)\n\t}\n\n\terr = writer.Close()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not close writer: %v\", err)\n\t}\n\n\tuploadUrl := httpHost + \"storage/upload\"\n\n\treq, err := http.NewRequest(\"POST\", uploadUrl, body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not create request: %v\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\n\tclient := &http.Client{\n\t\tTimeout: 120 * time.Minute,\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not send https request: %v\", err)","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/db/utils.go#L224-L260","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nbody := &bytes.Buffer{}\nwriter := multipart.NewWriter(body)\n...\nerr = writer.Close()\nif err != nil {\n\treturn \"\", fmt.Errorf(\"could not close writer: %v\", err)\n}\n// after\ntmpFile, err := os.CreateTemp(\"\", \"nhp-upload-*\")\nif err != nil {\n\treturn \"\", fmt.Errorf(\"could not create temp file: %v\", err)\n}\ndefer os.Remove(tmpFile.Name())\ndefer tmpFile.Close()\nwriter := multipart.NewWriter(tmpFile)\n...\nerr = writer.Close()\nif err != nil {\n\treturn \"\", fmt.Errorf(\"could not close writer: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"fi, err := os.Stat(filePath)\nif err != nil { return err }\nconst maxInMemory = 512 << 20 // 512MB\nif fi.Size() > maxInMemory {\n\treturn fmt.Errorf(\"file %s too large (%d bytes) for in-memory multipart upload\", filePath, fi.Size())\n}","typeGuard":"func canBufferInMemory(size int64, limit int64) bool {\n\treturn size > 0 && size <= limit\n}","tryCatchPattern":"result, err := device.UploadFileToNHPServer(filePath)\nif err != nil {\n\tvar memErr *errors.errorString\n\tif strings.Contains(err.Error(), \"could not close writer\") || strings.Contains(err.Error(), \"bytes.ErrTooLarge\") {\n\t\t// free memory / use disk-backed upload path, then retry\n\t}\n\treturn fmt.Errorf(\"upload failed: %w\", err)\n}","preventionTips":["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"],"tags":["go","multipart","http-upload","memory"],"backgroundTag":"file-write-failed","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}