nextcloud/server · error · Sabre\DAV\Exception\BadRequest

Boundary not found where it should be.

Error message

Boundary not found where it should be.

What it means

readBoundary() first verifies the stream cursor sits exactly before '--<boundary>\r\n' (via isAtBoundary()); a mismatch throws Sabre\DAV\Exception\BadRequest (HTTP 400). It means the multipart payload structure desynchronized from the boundary declared in the Content-Type header.

Source

Thrown at apps/dav/lib/BulkUpload/MultipartRequestParser.php:158

		$headers = $this->readPartHeaders();

		$length = (int)$headers['content-length'];

		$this->validateHash($length, $headers['x-file-md5'] ?? '', $headers['oc-checksum'] ?? '');
		$content = $this->readPartContent($length);

		return [$headers, $content];
	}

	/**
	 * Read the boundary and check its content.
	 *
	 * @throws BadRequest
	 */
	private function readBoundary(): string {
		if (!$this->isAtBoundary()) {
			throw new BadRequest('Boundary not found where it should be.');
		}

		return fread($this->stream, strlen($this->boundary));
	}

	/**
	 * Return the headers of a part of the multipart body.
	 *
	 * @throws Exception
	 * @throws BadRequest
	 * @throws LengthRequired
	 */
	private function readPartHeaders(): array {
		$headers = [];

		while (($line = fgets($this->stream)) !== "\r\n") {
			if ($line === false) {
				throw new Exception('An error occurred while reading headers of a part');

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Byte-inspect the exact request (curl --trace-ascii, tcpdump) and compare the boundary after 'boundary=' in Content-Type with the '--<boundary>\r\n' markers in the body
  2. Ensure every part ends with \r\n and is followed by '--<boundary>\r\n', and the body ends with '--<boundary>--\r\n'
  3. Stop building the body by string concatenation; use a multipart library or the desktop client's payload as reference (see tests/unit .../MultipartRequestParserTest.php fixtures)
  4. Verify no intermediary proxy or input handler strips CR bytes

Example fix

// before (LF-only, wrong framing)
$body = "--$boundary\nContent-Length: 3\n\nfoo\n--$boundary--\n";

// after (exact CRLF framing the parser expects)
$body = "--$boundary\r\nContent-Length: 3\r\nX-File-MD5: " . md5('foo') . "\r\n\r\nfoo\r\n--$boundary--\r\n";
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: assert the exact framing before sending
$marker = "--{$boundary}\r\n";
foreach ($parts as $i => $p) {
    $body = $p['raw'];
    if (!str_ends_with($body, "\r\n")) {
        throw new \InvalidArgumentException("Part {$i} must end with CRLF before the next boundary");
    }
}

Try / catch

// Client side: inspect the 400 JSON body; parsing errors abort the whole upload
// HTTP 400 + {"<path>": ...} from /dav/bulk means framing was rejected - fix payload, do not retry as-is

Prevention

When it happens

Trigger: POST to /dav/bulk where a part is not immediately followed by the exact boundary string: boundary in 'Content-Type: multipart/related; boundary=X' differs from the '--X\r\n' lines actually in the body, missing or extra CRLF between part content and the next boundary, LF-only line endings, or stray preamble bytes.

Common situations: Hand-rolled multipart writers using \n instead of \r\n; quoted or whitespace-padded boundary values handled differently by client and server; proxies rewriting line endings; copy-pasted example payloads with an edited boundary.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/13bcc6e9835ffbbb. Report an issue: GitHub.