{"record":{"id":"7166848f91093c49","repo":"Budibase/budibase","slug":"exactly-one-openapi-template-file-is-required","errorCode":null,"errorMessage":"Exactly one OpenAPI template file is required","messagePattern":"Exactly one OpenAPI template file is required","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/restTemplate.ts","lineNumber":60,"sourceCode":"  }\n  throw new HTTPError(\"OpenAPI template must be a YAML or JSON file\", 400)\n}\n\nexport const fetch = async (\n  ctx: UserCtx<void, FetchCustomRestTemplatesResponse>\n) => {\n  ctx.body = await sdk.restTemplates.fetch()\n}\n\nexport const upload = async (\n  ctx: UserCtx<\n    UploadCustomRestTemplateRequest,\n    UploadCustomRestTemplateResponse\n  >\n) => {\n  const file = ctx.request.files?.file\n  if (!file || Array.isArray(file)) {\n    throw new HTTPError(\"Exactly one OpenAPI template file is required\", 400)\n  }\n\n  const uploadDetails = getUploadDetails(file)\n  if (!uploadDetails) {\n    throw new HTTPError(\"Invalid OpenAPI template upload\", 400)\n  }\n\n  try {\n    const name = ctx.request.body.name\n    const description = ctx.request.body.description\n    if (typeof name !== \"string\" || !name.trim()) {\n      throw new HTTPError(\"Template name is required\", 400)\n    }\n    if (typeof description !== \"string\") {\n      throw new HTTPError(\"Template description is required\", 400)\n    }\n\n    const fileExtension = getFileExtension(uploadDetails.filename)","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/restTemplate.ts#L42-L78","documentation":"The `upload` controller reads `ctx.request.files?.file` and requires exactly one uploaded file: it must exist and must not be an array (multi-file upload). Missing or multiple files throw this 400 HTTPError before any processing occurs.","triggerScenarios":"POSTing the upload endpoint without a file field named `file`; attaching multiple files under the same field name (some clients wrap uploads in arrays); using a multipart client that names the field something else (e.g. `template`, `spec`).","commonSituations":"curl commands forgetting `-F file=@spec.yaml`; frontend FormData appending under the wrong key; API clients sending multiple files to bulk-import; omitting `Content-Type: multipart/form-data` so files never land on `ctx.request.files`.","solutions":["Attach exactly one file under the multipart field name `file` (e.g. `curl -F file=@spec.yaml ...`)","Ensure the request uses multipart/form-data encoding","Fix client code to append a single file: `formData.append(\"file\", theFile)`","Check server/file-upload middleware config so `ctx.request.files` is populated"],"exampleFix":"// before\nform.append(\"template\", fileInput.files[0])\n// after\nform.append(\"file\", fileInput.files[0])","handlingStrategy":"validation","validationCode":"const f = formData.get(\"file\")\nif (!(f instanceof File) || !f.size) {\n  throw new Error(\"Attach exactly one OpenAPI file under the 'file' field\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  await uploadRestTemplate(form)\n} catch (e) {\n  if (e.status === 400 && String(e.message).includes(\"Exactly one OpenAPI template file\")) {\n    // fix the multipart field name / file count and retry\n  }\n}","preventionTips":["Use the exact field name `file` in multipart requests","Limit the file input to a single selection in the UI","Verify multipart middleware is enabled server-side","Test uploads with curl -F before wiring the client"],"tags":["file-upload","rest-template","validation"],"backgroundTag":"missing-required-field","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}