{"record":{"id":"590e570b179ad3ea","repo":"langgenius/dify","slug":"no-file-uploaded","errorCode":"no_file_uploaded","errorMessage":"Please upload your file.","messagePattern":"Please upload your file\\.","errorType":"error_code","errorClass":"NoFileUploadedError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/app/annotation.py","lineNumber":468,"sourceCode":"    @console_ns.response(403, \"Insufficient permissions\")\n    @console_ns.response(400, \"No file uploaded or too many files\")\n    @console_ns.response(413, \"File too large\")\n    @console_ns.response(429, \"Too many requests or concurrent imports\")\n    @setup_required\n    @login_required\n    @account_initialization_required\n    @cloud_edition_billing_resource_check(\"annotation\")\n    @annotation_import_rate_limit\n    @annotation_import_concurrency_limit\n    @edit_permission_required\n    @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_EDIT)\n    @with_session\n    def post(self, session: Session, app_id: UUID):\n        from configs import dify_config\n\n        # check file\n        if \"file\" not in request.files:\n            raise NoFileUploadedError()\n\n        if len(request.files) > 1:\n            raise TooManyFilesError()\n\n        # get file from request\n        file = request.files[\"file\"]\n\n        # check file type\n        if not file.filename or not file.filename.lower().endswith(\".csv\"):\n            raise ValueError(\"Invalid file type. Only CSV files are allowed\")\n\n        # Check file size before processing\n        file.stream.seek(0, 2)  # Seek to end of file\n        file_size = file.stream.tell()\n        file.stream.seek(0)  # Reset to beginning\n\n        max_size_bytes = dify_config.ANNOTATION_IMPORT_FILE_SIZE_LIMIT * 1024 * 1024\n        if file_size > max_size_bytes:","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/app/annotation.py#L450-L486","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the request is multipart/form-data and includes exactly one file part under the field name 'file'.","Verify the client code: in JS use FormData and append('file', fileBlob, 'annotations.csv'); in cURL use -F 'file=@path/to/annotations.csv'.","Double-check that no proxy/gateway strips multipart bodies or renames form fields.","Confirm the file actually exists on disk before attaching (a missing source path can silently produce an empty form)."],"exampleFix":"// before\nconst res = await fetch(url, { method: 'POST', body: JSON.stringify({}) });\n\n// after\nconst form = new FormData();\nform.append('file', csvFile, 'annotations.csv');\nconst res = await fetch(url, { method: 'POST', body: form });","handlingStrategy":"validation","validationCode":"function buildAnnotationImportForm(file) {\n  if (!file) throw new Error('A CSV File is required');\n  const form = new FormData();\n  form.append('file', file, file.name || 'annotations.csv');\n  return form;\n}","typeGuard":"function isSingleCsvFile(files) {\n  return Array.isArray(files) ? files.length === 1 && files[0].name.toLowerCase().endsWith('.csv') : !!files && files.name.toLowerCase().endsWith('.csv');\n}","tryCatchPattern":"try {\n  const res = await fetch(url, { method: 'POST', body: form });\n  if (res.status === 400) {\n    const body = await res.json();\n    if (body.code === 'no_file_uploaded') showMessage('Attach a CSV file first.');\n  }\n} catch (e) { /* network */ }","preventionTips":["Always use FormData and append under the exact field name 'file'.","Assert the file object is non-null before constructing the request.","Set the request Content-Type implicitly via FormData (do not set it manually)."],"tags":["annotations","file-upload","multipart","console-api","validation"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}