{"record":{"id":"61d9166ab031a27b","repo":"nextcloud/server","slug":"an-error-occurred-while-reading-headers-of-a-part","errorCode":null,"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":500,"severity":"error","filePath":"apps/dav/lib/BulkUpload/MultipartRequestParser.php","lineNumber":176,"sourceCode":"\t\t\tthrow new BadRequest('Boundary not found where it should be.');\n\t\t}\n\n\t\treturn fread($this->stream, strlen($this->boundary));\n\t}\n\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}","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/BulkUpload/MultipartRequestParser.php#L158-L194","documentation":"readPartHeaders() loops fgets() until the blank line '\\r\\n' that terminates a part's header block; fgets() returning false means EOF was reached first. The body ended in the middle of a part's headers, and this generic Sabre\\DAV\\Exception (default code 500) is thrown; inside parseNextPart() it is caught by BulkUploadPlugin and returned to the client as HTTP 400.","triggerScenarios":"POST to /dav/bulk whose body is truncated inside a part's header block: the request ended early (request Content-Length larger than bytes actually sent, chunked transfer aborted, connection dropped) so the terminating '\\r\\n' blank line never arrives.","commonSituations":"Client timeouts or connection resets during large multi-file uploads; reverse proxy limits truncating request bodies (nginx client_max_body_size, Apache LimitRequestBody, proxy buffering); test harnesses sending incomplete fixtures.","solutions":["Retry with a tiny single-file bulk upload; if that succeeds the problem is size-, timeout-, or proxy-related","Raise/verify proxy limits and timeouts for the DAV endpoint (nginx client_max_body_size, proxy_read_timeout; Apache LimitRequestBody, TimeOut)","Make the client send a correct request Content-Length and complete the transfer; check access logs for 499/408/reset evidence","Capture the wire body server-side to confirm exactly where it stops mid-headers"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Client-side: send an explicit, correct Content-Length and finish the body\n$payload = buildMultipartBody($parts, $boundary);\n$ch = curl_init('https://host/remote.php/dav/bulk');\ncurl_setopt($ch, CURLOPT_POSTFIELDS, $payload); // sets Content-Length = strlen($payload)\ncurl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/related; boundary=' . $boundary]);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep each bulk upload small enough to finish within proxy and client timeouts","Verify request Content-Length equals actual bytes sent before the request leaves","Watch for proxy truncation (499/400 patterns) when uploads fail mid-body"],"tags":["php","webdav","nextcloud","bulk-upload","multipart","truncation","eof"],"backgroundTag":"truncated-request-body","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}