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

No hash provided.

Error message

No hash provided.

What it means

validateHash() needs either oc-checksum ('algo:hash') or x-file-md5 with a non-empty value. Reaching its else-branch means both were passed as empty strings - which happens when readPartHeaders()' isset() checks pass because the headers exist but hold empty values ('X-File-MD5:' with nothing after the colon).

Source

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

		// Read '\r\n'.
		stream_get_contents($this->stream, 2);

		return $content;
	}

	/**
	 * Compute the MD5 or checksum hash of the next x bytes.
	 * TODO: Drop $md5 argument when the latest desktop client that uses it is no longer supported.
	 */
	private function validateHash(int $length, string $fileMd5Header, string $checksumHeader): void {
		if ($checksumHeader !== '') {
			[$algorithm, $hash] = explode(':', $checksumHeader, 2);
		} elseif ($fileMd5Header !== '') {
			$algorithm = 'md5';
			$hash = $fileMd5Header;
		} else {
			throw new BadRequest('No hash provided.');
		}

		$context = hash_init($algorithm);
		hash_update_stream($context, $this->stream, $length);
		fseek($this->stream, -$length, SEEK_CUR);
		$computedHash = hash_final($context);
		if ($hash !== $computedHash) {
			throw new BadRequest("Computed $algorithm hash is incorrect ($computedHash).");
		}
	}
}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Always compute and send a real digest - the endpoint requires integrity verification, an empty value is as bad as none
  2. Only build the header line after the hash value is known, never emit a bare 'Header:' placeholder
  3. If you control a buggy middleware stripping header values, fix it there

Example fix

// before
"X-File-MD5: $maybeUndefinedVar\r\n" // emits 'X-File-MD5: ' when unset

// after
"X-File-MD5: " . hash('md5', $content) . "\r\n"
Defensive patterns

Strategy: validation

Validate before calling

// Never emit an empty hash header
if (!isset($md5) || $md5 === '') {
    $md5 = hash('md5', $content); // compute instead of sending blank
}
$partHeader = "X-File-MD5: {$md5}\r\n";

Prevention

When it happens

Trigger: A bulk-upload part with a hash header that is present but blank, e.g. 'X-File-MD5:\r\n' or 'OC-Checksum: \r\n'; isset() in readPartHeaders() lets it through, then validateHash() rejects it with HTTP 400.

Common situations: Template code emitting header names with unfilled placeholders; clients sending the header name unconditionally and only filling it when a checksum was computed.

Related errors


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