{"record":{"id":"3cf4039600c19100","repo":"vxcontrol/pentagi","slug":"flowfiles-invalidrequest","errorCode":"FlowFiles.InvalidRequest","errorMessage":"at least one uploaded file is required","messagePattern":"at least one uploaded file is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/pkg/server/services/flow_files.go","lineNumber":164,"sourceCode":"\t}\n\n\tc.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, flowfiles.MaxUploadRequestSize)\n\tmultipartForm, err := c.MultipartForm()\n\tif err != nil {\n\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"error reading multipart form\")\n\t\tresponse.Error(c, response.ErrFlowFilesInvalidRequest, err)\n\t\treturn\n\t}\n\n\tfileHeaders := multipartForm.File[\"files\"]\n\tif len(fileHeaders) == 0 {\n\t\tfileHeader, formErr := c.FormFile(\"file\")\n\t\tif formErr == nil && fileHeader != nil {\n\t\t\tfileHeaders = append(fileHeaders, fileHeader)\n\t\t}\n\t}\n\tif len(fileHeaders) == 0 {\n\t\terr = errors.New(\"at least one uploaded file is required\")\n\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"missing uploaded files\")\n\t\tresponse.Error(c, response.ErrFlowFilesInvalidRequest, err)\n\t\treturn\n\t}\n\tif len(fileHeaders) > flowfiles.MaxUploadFiles {\n\t\terr = fmt.Errorf(\"too many uploaded files: %d exceeds the maximum allowed count of %d\",\n\t\t\tlen(fileHeaders), flowfiles.MaxUploadFiles)\n\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"too many uploaded files\")\n\t\tresponse.Error(c, response.ErrFlowFilesInvalidRequest, err)\n\t\treturn\n\t}\n\n\tuploadDir := s.flowUploadsDir(flowID)\n\tif err := os.MkdirAll(uploadDir, 0755); err != nil {\n\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"error creating upload directory\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn\n\t}","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/flow_files.go#L146-L182","documentation":"UploadFlowFiles requires multipart form parts named 'file'; it collects all such parts and rejects the request with FlowFiles.InvalidRequest when none were provided. The endpoint is strictly multi-upload, so an empty upload is a client-side request error rather than a server fault.","triggerScenarios":"POST /flows/{flow_id}/files with no 'file' multipart part — e.g. the field is named 'files', 'upload', or 'attachment', or the body was sent as JSON/base64 instead of multipart/form-data.","commonSituations":"curl -F field-name mismatches; frontend FormData appending under the wrong key; proxies or clients stripping the multipart body; sending an empty FormData object.","solutions":["Attach at least one multipart part named exactly 'file': curl -F \"file=@./report.txt\" https://host/api/v1/flows/<id>/files","Verify the Content-Type is multipart/form-data (let the HTTP client set the boundary)","Check the frontend appends to FormData with key 'file' (repeat the key for multiple files)","Confirm no middleware rewrites or drops the request body"],"exampleFix":"// before\ncurl -X POST https://host/api/v1/flows/42/files -F \"upload=@a.txt\"\n// after\ncurl -X POST https://host/api/v1/flows/42/files -F \"file=@a.txt\" -F \"file=@b.txt\"","handlingStrategy":"validation","validationCode":"function assertHasFiles(files: FileList | File[] | null): asserts files is NonNullable<typeof files> {\n  if (!files || files.length === 0) throw new Error('select at least one file');\n}\nconst fd = new FormData();\nfor (const f of files) fd.append('file', f); // key must be exactly 'file'","typeGuard":"function hasMultipartFiles(fd: FormData): boolean {\n  return fd.getAll('file').length > 0;\n}","tryCatchPattern":"try {\n  await api.post(`/flows/${flowId}/files`, fd, { headers: { 'Content-Type': 'multipart/form-data' } });\n} catch (e) {\n  if (e.response?.data?.code === 'FlowFiles.InvalidRequest') {\n    alert('Please attach at least one file before uploading.');\n    return;\n  }\n  throw e;\n}","preventionTips":["Always append multipart parts under the exact key 'file'","Disable the upload button until files are selected","Never send file uploads as JSON bodies","Respect flowfiles.MaxUploadFiles on the client side"],"tags":["api","validation","file-upload","multipart"],"backgroundTag":"missing-required-file-upload","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}