{"record":{"id":"7ff273d2ae2eef9e","repo":"nextcloud/server","slug":"400","errorCode":"400","errorMessage":"An error occurred while reading headers of a part","messagePattern":"An error occurred while reading headers of a part","errorType":"exception","errorClass":"Sabre\\DAV\\Exception","httpStatus":400,"severity":"error","filePath":"apps/dav/lib/BulkUpload/MultipartRequestParser.php","lineNumber":181,"sourceCode":"\n\t/**\n\t * Return the headers of a part of the multipart body.\n\t *\n\t * @throws Exception\n\t * @throws BadRequest\n\t * @throws LengthRequired\n\t */\n\tprivate function readPartHeaders(): array {\n\t\t$headers = [];\n\n\t\twhile (($line = fgets($this->stream)) !== \"\\r\\n\") {\n\t\t\tif ($line === false) {\n\t\t\t\tthrow new Exception('An error occurred while reading headers of a part');\n\t\t\t}\n\n\t\t\tif (!str_contains($line, ':')) {\n\t\t\t\t$this->logger->error('Header missing \":\" on bulk request: ' . json_encode($line));\n\t\t\t\tthrow new Exception('An error occurred while reading headers of a part', Http::STATUS_BAD_REQUEST);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t[$key, $value] = explode(':', $line, 2);\n\t\t\t\t$headers[strtolower(trim($key))] = trim($value);\n\t\t\t} catch (\\Exception $e) {\n\t\t\t\tthrow new BadRequest('An error occurred while parsing headers of a part', Http::STATUS_BAD_REQUEST, $e);\n\t\t\t}\n\t\t}\n\n\t\tif (!isset($headers['content-length'])) {\n\t\t\tthrow new LengthRequired('The Content-Length header must not be null.');\n\t\t}\n\n\t\t// TODO: Drop $md5 condition when the latest desktop client that uses it is no longer supported.\n\t\tif (!isset($headers['x-file-md5']) && !isset($headers['oc-checksum'])) {\n\t\t\tthrow new BadRequest('The hash headers must not be null.');\n\t\t}","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/BulkUpload/MultipartRequestParser.php#L163-L199","documentation":"While reading a part's header lines, any line without a ':' cannot be a header and is rejected with HTTP 400. The offending line is logged first ('Header missing \":\" on bulk request: ...' in nextcloud.log), which tells you the exact malformed input.","triggerScenarios":"A bulk-upload part containing a header line that is not 'Key: value': a folded/continuation line starting with space or tab, a bare word, or a line whose colon was lost (often due to wrong line-ending handling splitting lines incorrectly).","commonSituations":"Hand-built multipart bodies; clients emitting RFC 7230 obs-fold style headers that arrive as continuation lines; charset corruption mangling the ':' byte; header names or values with control characters.","solutions":["Open nextcloud.log and read the logged json_encode($line) entry; it shows the exact malformed line and its bytes","Fix the client to emit one flat 'Header-Name: value\\r\\n' line per header, no folding, no bare lines","Keep headers ASCII with a single colon; regenerate the payload with a maintained multipart library instead of string templates"],"exampleFix":"// before\n\"X-File-Path: /a.txt\\r\\n\"\n.\"  continued-wrongly\\r\\n\" // continuation line has no ':' -> this error\n\n// after\n\"X-File-Path: /a.txt\\r\\n\" // one flat header per line only","handlingStrategy":"validation","validationCode":"// Validate header lines before writing them into the part\nforeach ($headers as $key => $value) {\n    if (!preg_match('/^[!#$%&\\'*+.^_`|~0-9A-Za-z-]+$/', $key)) {\n        throw new \\InvalidArgumentException(\"Invalid header name: {$key}\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Emit one flat 'Header-Name: value\\r\\n' per line; no folding, no bare lines","Check nextcloud.log for 'Header missing' entries - it logs the exact offending line","Test custom clients against the MultipartRequestParser unit fixtures first"],"tags":["php","webdav","nextcloud","bulk-upload","multipart","http-headers"],"backgroundTag":"malformed-header-line","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}