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

The hash headers must not be null.

Error message

The hash headers must not be null.

What it means

Each bulk-upload part must carry an integrity hash: either 'X-File-MD5' (legacy desktop client) or 'OC-Checksum' in 'algorithm:value' form. If neither header is present in the part, readPartHeaders() rejects it with HTTP 400 'The hash headers must not be null.' - the endpoint never accepts unverified part content.

Source

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

				$this->logger->error('Header missing ":" on bulk request: ' . json_encode($line));
				throw new Exception('An error occurred while reading headers of a part', Http::STATUS_BAD_REQUEST);
			}

			try {
				[$key, $value] = explode(':', $line, 2);
				$headers[strtolower(trim($key))] = trim($value);
			} catch (\Exception $e) {
				throw new BadRequest('An error occurred while parsing headers of a part', Http::STATUS_BAD_REQUEST, $e);
			}
		}

		if (!isset($headers['content-length'])) {
			throw new LengthRequired('The Content-Length header must not be null.');
		}

		// TODO: Drop $md5 condition when the latest desktop client that uses it is no longer supported.
		if (!isset($headers['x-file-md5']) && !isset($headers['oc-checksum'])) {
			throw new BadRequest('The hash headers must not be null.');
		}

		return $headers;
	}

	/**
	 * Return the content of a part of the multipart body.
	 *
	 * @throws Exception
	 * @throws BadRequest
	 */
	private function readPartContent(int $length): string {
		if ($length === 0) {
			$content = '';
		} else {
			$content = stream_get_line($this->stream, $length);
		}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Send 'X-File-MD5: <lowercase hex md5>' per part, or preferably 'OC-Checksum: SHA1:<hex>' (any hash_init() algorithm works)
  2. Compute the digest over exactly the bytes that form the part body, before boundary and CRLF
  3. Cross-check your part layout with the parser's unit tests or a desktop client capture

Example fix

// before
$headers = "X-File-Path: /a.txt\r\nContent-Length: 3\r\n\r\n";

// after
$headers = "X-File-Path: /a.txt\r\nContent-Length: 3\r\n"
    . "X-File-MD5: " . md5($content) . "\r\n\r\n";
Defensive patterns

Strategy: validation

Validate before calling

// Build the hash header for every part, computed from the exact bytes
$part['headers']['x-file-md5'] = hash('md5', $part['content']);
// or: $part['headers']['oc-checksum'] = 'SHA1:' . hash('sha1', $part['content']);

Prevention

When it happens

Trigger: POST to /dav/bulk with a part whose headers contain neither x-file-md5 nor oc-checksum; e.g. a third-party client implementing the endpoint from an outdated spec, or a payload assembler that drops these headers.

Common situations: Custom sync scripts and integrations hitting POST /dav/bulk for the first time; clients that assumed checksums were optional; header typos like 'X-File-MD5 ' or 'OC-Checksums'.

Related errors


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