{"record":{"id":"42eb7c0e801eaac3","repo":"vxcontrol/pentagi","slug":"errresourcesinvalidrequest","errorCode":"ErrResourcesInvalidRequest","errorMessage":"at least one file is required","messagePattern":"at least one file is required","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/pkg/server/services/resources.go","lineNumber":268,"sourceCode":"\t\treturn\n\t}\n\n\tc.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, resources.MaxUploadRequestSize)\n\tmultipartForm, err := c.MultipartForm()\n\tif err != nil {\n\t\tlogger.FromContext(c).WithError(err).Error(\"error reading multipart form for resources upload\")\n\t\tresponse.Error(c, response.ErrResourcesInvalidRequest, err)\n\t\treturn\n\t}\n\n\tfileHeaders := multipartForm.File[\"files\"]\n\tif len(fileHeaders) == 0 {\n\t\tif fh, ferr := c.FormFile(\"file\"); ferr == nil && fh != nil {\n\t\t\tfileHeaders = append(fileHeaders, fh)\n\t\t}\n\t}\n\tif len(fileHeaders) == 0 {\n\t\tresponse.Error(c, response.ErrResourcesInvalidRequest, errors.New(\"at least one file is required\"))\n\t\treturn\n\t}\n\tif len(fileHeaders) > resources.MaxUploadFiles {\n\t\tresponse.Error(c, response.ErrResourcesInvalidRequest,\n\t\t\tfmt.Errorf(\"too many files: %d exceeds the limit of %d\", len(fileHeaders), resources.MaxUploadFiles))\n\t\treturn\n\t}\n\n\tif err := resources.EnsureResourcesDir(s.dataDir); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Error(\"failed to ensure resources directory\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn\n\t}\n\tblobsDir := resources.ResourcesDir(s.dataDir)\n\n\tvar totalSize int64\n\tpendingList := make([]pendingResourceUpload, 0, len(fileHeaders))\n\tseenPaths := make(map[string]struct{}, len(fileHeaders))","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/resources.go#L250-L286","documentation":"UploadResources requires at least one file in the multipart request. The handler first looks for a `files` (multi-file) field, then falls back to a single `file` field; if neither yields a file header, it returns ErrResourcesInvalidRequest with 'at least one file is required'. There is also an upper bound of resources.MaxUploadFiles.","triggerScenarios":"POSTing the upload endpoint with an empty multipart body, using the wrong form field name (neither `files` nor `file`), sending files as raw body instead of multipart/form-data, or all entries present but empty.","commonSituations":"Client library serializes files under a different field name; curl invocation omits -F; frontend form not including the File objects; content-type not multipart so FormFile parses nothing.","solutions":["Attach at least one file via multipart form field `files` (or a single `file` field) in the POST request","Verify the request Content-Type is multipart/form-data and the client actually appends the File/Blob objects","Check file count stays within resources.MaxUploadFiles"],"exampleFix":"// before\ncurl -X POST https://localhost:8443/api/v1/resources -H 'Authorization: Bearer ...'  # no file sent\n// after\ncurl -X POST https://localhost:8443/api/v1/resources -H 'Authorization: Bearer ...' -F 'files=@report.pdf'","handlingStrategy":"validation","validationCode":"if (!files || files.length === 0) throw new Error('select at least one file before uploading');\nif (files.length > MAX_UPLOAD_FILES) throw new Error('too many files');","typeGuard":"function hasFiles(f: FormData): boolean {\n  return f.getAll('files').length > 0 || f.get('file') !== null;\n}","tryCatchPattern":"try {\n  await api.post('/resources', formData, { headers: { 'Content-Type': 'multipart/form-data' } });\n} catch (e) {\n  if (e.response?.data?.code === 'ErrResourcesInvalidRequest' && /file is required/.test(e.response.data.message)) {\n    // prompt user to pick a file\n  }\n}","preventionTips":["Always append File objects to FormData under `files` (or `file`)","Disable the upload button until files.length > 0","Confirm Content-Type is multipart/form-data and the client is not sending JSON"],"tags":["rest","upload","multipart","validation"],"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"}