{"record":{"id":"efa5abab7a021193","repo":"NginxProxyManager/nginx-proxy-manager","slug":"no-files-were-uploaded","errorCode":null,"errorMessage":"No files were uploaded","messagePattern":"No files were uploaded","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/routes/nginx/certificates.js","lineNumber":159,"sourceCode":" * Validate Certs before saving\n *\n * /api/nginx/certificates/validate\n */\nrouter\n\t.route(\"/validate\")\n\t.options((_, res) => {\n\t\tres.sendStatus(204);\n\t})\n\t.all(jwtdecode())\n\n\t/**\n\t * POST /api/nginx/certificates/validate\n\t *\n\t * Validate certificates\n\t */\n\t.post(async (req, res, next) => {\n\t\tif (!req.files) {\n\t\t\tres.status(400).send({ error: \"No files were uploaded\" });\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await internalCertificate.validate({\n\t\t\t\tfiles: req.files,\n\t\t\t});\n\t\t\tres.status(200).send(result);\n\t\t} catch (err) {\n\t\t\tdebug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);\n\t\t\tnext(err);\n\t\t}\n\t});\n\n/**\n * Specific certificate\n *\n * /api/nginx/certificates/123","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/NginxProxyManager/nginx-proxy-manager/blob/934a3fafe5ae82d752f0a18c0f1d0eb050296730/backend/routes/nginx/certificates.js#L141-L177","documentation":"This 400 comes from POST /api/nginx/certificates/validate, which uses multipart file uploads (express-fileupload style req.files). If the request contains no file parts, req.files is undefined/null and the route rejects it immediately with \"No files were uploaded\" before calling internalCertificate.validate.","triggerScenarios":"Sending the request without a multipart/form-data body, sending form fields but no files, misnamed file part (files are looked up by field name later), or a client that sent application/json instead of multipart so req.files was never populated.","commonSituations":"curl invocations missing -F flags, frontend forms where the file input is optional/empty and submits anyway, proxies or body parsers stripping multipart parts, or the file input's field name not matching what the handler expects.","solutions":["Send the request as multipart/form-data including at least one file part: curl -F 'certificate=@cert.pem' -F 'certificate_key=@key.pem' ...","Make the file input required in the UI and skip submitting the validate call when no file is selected","Confirm the upload middleware (express-fileupload) is mounted on the route and no earlier middleware consumed the body"],"exampleFix":"// before\ncurl -X POST https://npm.example.com/api/nginx/certificates/validate \\\n  -H 'Content-Type: application/json' -d '{}'\n\n// after\ncurl -X POST https://npm.example.com/api/nginx/certificates/validate \\\n  -F 'certificate=@fullchain.pem' \\\n  -F 'certificate_key=@privkey.pem'","handlingStrategy":"validation","validationCode":"const hasFiles = (req) =>\n  !!req.files && Object.keys(req.files).length > 0;\n\nif (!hasFiles(req)) {\n  return res.status(400).send({ error: 'No files were uploaded' });\n}\n// proceed to internalCertificate.validate({ files: req.files, ... })","typeGuard":"type FileUploads = Record<string, UploadedFile>;\n\nconst hasUploadedFiles = (files: FileUploads | null | undefined): files is FileUploads =>\n  !!files && Object.keys(files).length > 0;","tryCatchPattern":"const res = await fetch('/api/nginx/certificates/validate', { method: 'POST', body: form });\nif (res.status === 400) {\n  const body = await res.json();\n  if (body.error === 'No files were uploaded') {\n    // tell the user to attach cert/key files; do not retry\n  }\n}","preventionTips":["Require the file inputs client-side before enabling submit","Always construct FormData with the exact field names the route expects","Smoke-test multipart flows through the real proxy chain (nginx limits included) in CI"],"tags":["nginx-proxy-manager","file-upload","multipart","http-400","certificates"],"backgroundTag":"multipart-file-missing","analyzedSha":"934a3fafe5ae82d752f0a18c0f1d0eb050296730","analyzedAt":"2026-08-27T14:34:22.258Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}