{"record":{"id":"90413995cb9a57e2","repo":"bitwarden/server","slug":"missing-content-type-boundary","errorCode":null,"errorMessage":"Missing content-type boundary.","messagePattern":"Missing content-type boundary\\.","errorType":"exception","errorClass":"InvalidDataException","httpStatus":400,"severity":"error","filePath":"src/Api/Utilities/MultipartFormDataHelper.cs","lineNumber":130,"sourceCode":"            if (ContentDispositionHeaderValue.TryParse(dataSection.ContentDisposition, out var dataContent)\n                && HasFileContentDisposition(dataContent))\n            {\n                using (dataSection.Body)\n                {\n                    await callback(dataSection.Body);\n                }\n            }\n            dataSection = null;\n        }\n    }\n\n\n    private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)\n    {\n        var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);\n        if (StringSegment.IsNullOrEmpty(boundary))\n        {\n            throw new InvalidDataException(\"Missing content-type boundary.\");\n        }\n\n        if (boundary.Length > lengthLimit)\n        {\n            throw new InvalidDataException($\"Multipart boundary length limit {lengthLimit} exceeded.\");\n        }\n\n        return boundary.ToString();\n    }\n\n    private static bool HasFileContentDisposition(ContentDispositionHeaderValue content)\n    {\n        // Content-Disposition: form-data; name=\"data\"; filename=\"Misc 002.jpg\"\n        return content != null && content.DispositionType.Equals(\"form-data\") &&\n            (!StringSegment.IsNullOrEmpty(content.FileName) || !StringSegment.IsNullOrEmpty(content.FileNameStar));\n    }\n\n    private static bool HasDispositionName(ContentDispositionHeaderValue content, string name)","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Utilities/MultipartFormDataHelper.cs#L112-L148","documentation":"MultipartFormDataHelper.GetBoundary parses the request's Content-Type for a `boundary` parameter so it can delimit multipart/form-data parts (file uploads, e.g. Send attachments / imports). If the Content-Type carries no boundary token, the parts cannot be separated and the request fails with InvalidDataException (HTTP 400).","triggerScenarios":"A POST with header `Content-Type: multipart/form-data` but no `boundary=...` clause — typically a hand-set header that bypassed the HTTP library's boundary generation.","commonSituations":"Manually constructed fetch/HttpClient calls where the header is set statically; a reverse proxy or gateway stripping the boundary; using FormData but overriding its Content-Type.","solutions":["Do not set Content-Type yourself — let the HTTP client (FormData / MultipartFormDataContent) generate and append the boundary.","If you must set it manually, append `; boundary=----YourBoundary`.","Check that no proxy between client and server rewrites or strips the Content-Type header."],"exampleFix":"// before\nfetch(url, { method: 'POST', headers: { 'Content-Type': 'multipart/form-data' }, body: form })\n// after\nfetch(url, { method: 'POST', body: form }) // browser sets Content-Type + boundary","handlingStrategy":"validation","validationCode":"function assertBoundaryPresent(contentType) {\n  if (!/boundary=.+/i.test(contentType ?? '')) {\n    throw new Error('Content-Type multipart/form-data is missing a boundary; let the HTTP client set it.');\n  }\n}","typeGuard":"function hasMultipartBoundary(contentType: string | null | undefined): boolean {\n  return !!contentType && /boundary=[^;\\s]+/i.test(contentType);\n}","tryCatchPattern":null,"preventionTips":["Never hard-set Content-Type for FormData; let the browser/HttpClient generate the boundary.","If using MultipartFormDataContent, do not override the Content-Type header manually.","Verify proxies/gateways forward the full Content-Type unchanged."],"tags":["multipart","upload","content-type","http","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}