vxcontrol/pentagi · error

ErrResourcesInvalidRequest

ErrResourcesInvalidRequest

Error message

at least one file is required

What it means

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.

Solutions

  1. Attach at least one file via multipart form field `files` (or a single `file` field) in the POST request
  2. Verify the request Content-Type is multipart/form-data and the client actually appends the File/Blob objects
  3. Check file count stays within resources.MaxUploadFiles

Example fix

// before
curl -X POST https://localhost:8443/api/v1/resources -H 'Authorization: Bearer ...'  # no file sent
// after
curl -X POST https://localhost:8443/api/v1/resources -H 'Authorization: Bearer ...' -F 'files=@report.pdf'
Defensive patterns

Strategy: validation

Validate before calling

if (!files || files.length === 0) throw new Error('select at least one file before uploading');
if (files.length > MAX_UPLOAD_FILES) throw new Error('too many files');

Type guard

function hasFiles(f: FormData): boolean {
  return f.getAll('files').length > 0 || f.get('file') !== null;
}

Try / catch

try {
  await api.post('/resources', formData, { headers: { 'Content-Type': 'multipart/form-data' } });
} catch (e) {
  if (e.response?.data?.code === 'ErrResourcesInvalidRequest' && /file is required/.test(e.response.data.message)) {
    // prompt user to pick a file
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/42eb7c0e801eaac3. Report an issue: GitHub.

Appendix: source

Thrown at backend/pkg/server/services/resources.go:268

		return
	}

	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, resources.MaxUploadRequestSize)
	multipartForm, err := c.MultipartForm()
	if err != nil {
		logger.FromContext(c).WithError(err).Error("error reading multipart form for resources upload")
		response.Error(c, response.ErrResourcesInvalidRequest, err)
		return
	}

	fileHeaders := multipartForm.File["files"]
	if len(fileHeaders) == 0 {
		if fh, ferr := c.FormFile("file"); ferr == nil && fh != nil {
			fileHeaders = append(fileHeaders, fh)
		}
	}
	if len(fileHeaders) == 0 {
		response.Error(c, response.ErrResourcesInvalidRequest, errors.New("at least one file is required"))
		return
	}
	if len(fileHeaders) > resources.MaxUploadFiles {
		response.Error(c, response.ErrResourcesInvalidRequest,
			fmt.Errorf("too many files: %d exceeds the limit of %d", len(fileHeaders), resources.MaxUploadFiles))
		return
	}

	if err := resources.EnsureResourcesDir(s.dataDir); err != nil {
		logger.FromContext(c).WithError(err).Error("failed to ensure resources directory")
		response.Error(c, response.ErrInternal, err)
		return
	}
	blobsDir := resources.ResourcesDir(s.dataDir)

	var totalSize int64
	pendingList := make([]pendingResourceUpload, 0, len(fileHeaders))
	seenPaths := make(map[string]struct{}, len(fileHeaders))

View on GitHub (pinned to ea665308ba)