calcom/cal.diy · warning · HttpError

Bad Request (Invalid Multi Form Data)

Error message

Bad Request (Invalid Multi Form Data)

What it means

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.

Source

Thrown at apps/web/app/api/parseRequestData.ts:26

export async function parseUrlFormData(req: NextRequest): Promise<Record<string, any>> {
  try {
    // Read raw text body (because Next.js does NOT parse x-www-form-urlencoded automatically)
    const rawBody = await req.text();
    const params = new URLSearchParams(rawBody);
    return Object.fromEntries(params);
  } catch (e) {
    log.error(`Invalid Url Form Data: ${e} from path ${req.nextUrl}`);
    throw new HttpError({ statusCode: 400, message: "Bad Request (Invalid Url Form Data)" });
  }
}

export async function parseMultiFormData(req: NextRequest): Promise<Record<string, any>> {
  try {
    const formData = await req.formData();
    return Object.fromEntries(formData.entries());
  } catch (e) {
    log.error(`Invalid Multi Form Data: ${e} from path ${req.nextUrl}`);
    throw new HttpError({ statusCode: 400, message: "Bad Request (Invalid Multi Form Data)" });
  }
}

export async function parseRequestData(req: NextRequest): Promise<Record<string, any>> {
  const contentType = req.headers.get("content-type") ?? "application/json";
  if (contentType.includes("application/json")) {
    try {
      return await req.json();
    } catch (e) {
      log.error(`Invalid JSON: ${e} from path ${req.nextUrl}`);
      throw new HttpError({ statusCode: 400, message: "Bad Request (Invalid JSON)" });
    }
  }

  if (contentType.includes("application/x-www-form-urlencoded")) {
    return await parseUrlFormData(req);
  }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Use a FormData object on the client so the browser sets a correct boundary automatically.
  2. Confirm no middleware reads the body before parseMultiFormData; Next.js route handlers own the body stream.
  3. Raise server body-size limits if large uploads are expected, and verify the proxy preserves the multipart boundary.
  4. Log the underlying error to distinguish boundary issues from stream-consumption issues.

Example fix

// before
await fetch('/api/upload', {
  method: 'POST',
  headers: { 'Content-Type': 'multipart/form-data; boundary=...' }, // hand-rolled
  body: rawBody,
});

// after
const fd = new FormData();
fd.append('file', fileInput.files[0]);
await fetch('/api/upload', { method: 'POST', body: fd }); // browser sets boundary
Defensive patterns

Strategy: try-catch

Validate before calling

// Let the browser build the multipart body
const fd = new FormData();
fd.append('file', file);
await fetch('/api/upload', { method: 'POST', body: fd }); // browser sets boundary

Type guard

function looksLikeMultipartContentType(ct: string): boolean {
  return /^multipart\/form-data;\s*boundary=.+/.test(ct);
}

Try / catch

try {
  await parseMultiFormData(req);
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 400) {
    return Response.json({ error: 'Malformed multipart upload' }, { status: 400 });
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Missing or incorrect multipart boundary, client abort mid-upload, request body already read by body-parser middleware, reverse proxy stripping the boundary.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/eba7c335eee11ee3. Report an issue: GitHub.