{"record":{"id":"eba7c335eee11ee3","repo":"calcom/cal.diy","slug":"bad-request-invalid-multi-form-data","errorCode":null,"errorMessage":"Bad Request (Invalid Multi Form Data)","messagePattern":"Bad Request \\(Invalid Multi Form Data\\)","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"warning","filePath":"apps/web/app/api/parseRequestData.ts","lineNumber":26,"sourceCode":"export async function parseUrlFormData(req: NextRequest): Promise<Record<string, any>> {\n  try {\n    // Read raw text body (because Next.js does NOT parse x-www-form-urlencoded automatically)\n    const rawBody = await req.text();\n    const params = new URLSearchParams(rawBody);\n    return Object.fromEntries(params);\n  } catch (e) {\n    log.error(`Invalid Url Form Data: ${e} from path ${req.nextUrl}`);\n    throw new HttpError({ statusCode: 400, message: \"Bad Request (Invalid Url Form Data)\" });\n  }\n}\n\nexport async function parseMultiFormData(req: NextRequest): Promise<Record<string, any>> {\n  try {\n    const formData = await req.formData();\n    return Object.fromEntries(formData.entries());\n  } catch (e) {\n    log.error(`Invalid Multi Form Data: ${e} from path ${req.nextUrl}`);\n    throw new HttpError({ statusCode: 400, message: \"Bad Request (Invalid Multi Form Data)\" });\n  }\n}\n\nexport async function parseRequestData(req: NextRequest): Promise<Record<string, any>> {\n  const contentType = req.headers.get(\"content-type\") ?? \"application/json\";\n  if (contentType.includes(\"application/json\")) {\n    try {\n      return await req.json();\n    } catch (e) {\n      log.error(`Invalid JSON: ${e} from path ${req.nextUrl}`);\n      throw new HttpError({ statusCode: 400, message: \"Bad Request (Invalid JSON)\" });\n    }\n  }\n\n  if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n    return await parseUrlFormData(req);\n  }\n","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/web/app/api/parseRequestData.ts#L8-L44","documentation":"Thrown by parseMultiFormData (HttpError, HTTP 400) when req.formData() rejects for a multipart/form-data request. A log.error records the exception and path. It signals the multipart payload was malformed or unreadable.","triggerScenarios":"POST with Content-Type multipart/form-data whose body is missing/corrupted: no boundary, truncated chunks, oversized field, or the body stream was already consumed.","commonSituations":"Missing or incorrect multipart boundary, client abort mid-upload, request body already read by body-parser middleware, reverse proxy stripping the boundary.","solutions":["Use a FormData object on the client so the browser sets a correct boundary automatically.","Confirm no middleware reads the body before parseMultiFormData; Next.js route handlers own the body stream.","Raise server body-size limits if large uploads are expected, and verify the proxy preserves the multipart boundary.","Log the underlying error to distinguish boundary issues from stream-consumption issues."],"exampleFix":"// before\nawait fetch('/api/upload', {\n  method: 'POST',\n  headers: { 'Content-Type': 'multipart/form-data; boundary=...' }, // hand-rolled\n  body: rawBody,\n});\n\n// after\nconst fd = new FormData();\nfd.append('file', fileInput.files[0]);\nawait fetch('/api/upload', { method: 'POST', body: fd }); // browser sets boundary","handlingStrategy":"try-catch","validationCode":"// Let the browser build the multipart body\nconst fd = new FormData();\nfd.append('file', file);\nawait fetch('/api/upload', { method: 'POST', body: fd }); // browser sets boundary","typeGuard":"function looksLikeMultipartContentType(ct: string): boolean {\n  return /^multipart\\/form-data;\\s*boundary=.+/.test(ct);\n}","tryCatchPattern":"try {\n  await parseMultiFormData(req);\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 400) {\n    return Response.json({ error: 'Malformed multipart upload' }, { status: 400 });\n  }\n  throw e;\n}","preventionTips":["Never set the multipart Content-Type/boundary manually; let FormData do it.","Raise body-size limits for expected large uploads.","Ensure the body stream is read exactly once in the route handler."],"tags":["request-parsing","form-data","multipart","upload","validation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}