langgenius/dify · error · NoFileUploadedError

no_file_uploaded

no_file_uploaded

Error message

Please upload your file.

What it means

Raised by POST /console/apps/{app_id}/annotations/batch-import (AnnotationBatchImportApi.post) when the multipart request contains no entry under the 'file' key in request.files. The controller guards this before any parsing, rate-limit, or billing work. It surfaces as a 400 with error_code 'no_file_uploaded' via NoFileUploadedError (BaseHTTPException).

Source

Thrown at api/controllers/console/app/annotation.py:468

    @console_ns.response(403, "Insufficient permissions")
    @console_ns.response(400, "No file uploaded or too many files")
    @console_ns.response(413, "File too large")
    @console_ns.response(429, "Too many requests or concurrent imports")
    @setup_required
    @login_required
    @account_initialization_required
    @cloud_edition_billing_resource_check("annotation")
    @annotation_import_rate_limit
    @annotation_import_concurrency_limit
    @edit_permission_required
    @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_EDIT)
    @with_session
    def post(self, session: Session, app_id: UUID):
        from configs import dify_config

        # check file
        if "file" not in request.files:
            raise NoFileUploadedError()

        if len(request.files) > 1:
            raise TooManyFilesError()

        # get file from request
        file = request.files["file"]

        # check file type
        if not file.filename or not file.filename.lower().endswith(".csv"):
            raise ValueError("Invalid file type. Only CSV files are allowed")

        # Check file size before processing
        file.stream.seek(0, 2)  # Seek to end of file
        file_size = file.stream.tell()
        file.stream.seek(0)  # Reset to beginning

        max_size_bytes = dify_config.ANNOTATION_IMPORT_FILE_SIZE_LIMIT * 1024 * 1024
        if file_size > max_size_bytes:

View on GitHub (pinned to ef8544b173)

Solutions

  1. Ensure the request is multipart/form-data and includes exactly one file part under the field name 'file'.
  2. Verify the client code: in JS use FormData and append('file', fileBlob, 'annotations.csv'); in cURL use -F 'file=@path/to/annotations.csv'.
  3. Double-check that no proxy/gateway strips multipart bodies or renames form fields.
  4. Confirm the file actually exists on disk before attaching (a missing source path can silently produce an empty form).

Example fix

// before
const res = await fetch(url, { method: 'POST', body: JSON.stringify({}) });

// after
const form = new FormData();
form.append('file', csvFile, 'annotations.csv');
const res = await fetch(url, { method: 'POST', body: form });
Defensive patterns

Strategy: validation

Validate before calling

function buildAnnotationImportForm(file) {
  if (!file) throw new Error('A CSV File is required');
  const form = new FormData();
  form.append('file', file, file.name || 'annotations.csv');
  return form;
}

Type guard

function isSingleCsvFile(files) {
  return Array.isArray(files) ? files.length === 1 && files[0].name.toLowerCase().endsWith('.csv') : !!files && files.name.toLowerCase().endsWith('.csv');
}

Try / catch

try {
  const res = await fetch(url, { method: 'POST', body: form });
  if (res.status === 400) {
    const body = await res.json();
    if (body.code === 'no_file_uploaded') showMessage('Attach a CSV file first.');
  }
} catch (e) { /* network */ }

Prevention

When it happens

Trigger: Calling the annotation batch-import endpoint with a request body that omits the file part entirely, or sends the file under a different field name (e.g., 'csv', 'upload'). Also triggered by sending application/json instead of multipart/form-data, since Flask leaves request.files empty.

Common situations: Frontend uploader attaches the File object under the wrong form field name; cURL/Postman request sent as JSON body rather than multipart form-data; automated script forgetting to open the file in binary 'rb' mode; the file input being disabled or skipped in the UI.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/590e570b179ad3ea. Report an issue: GitHub.