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

Computed $algorithm hash is incorrect ($computedHash).

Error message

Computed $algorithm hash is incorrect ($computedHash).

What it means

validateHash() hashes exactly Content-Length bytes of the part with the declared algorithm (md5 from X-File-MD5, or the algorithm prefix of OC-Checksum) and compares strings with ===; a mismatch throws HTTP 400 including the server-side computed digest. The bytes received hashed to something other than what the header claims.

Source

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

	 * 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. Recompute the hash immediately before sending, over the exact bytes of the part body, with the algorithm matching the header label
  2. Use lowercase hex (PHP hash()/hash_file() output) - the server compares raw strings case-sensitively
  3. Compare the server's computed value from the error message with your local digest; if both are stable but different, byte-diff the uploaded payload to find where content diverges (proxy or encoding mangling)

Example fix

// before
"OC-Checksum: SHA1:" . md5($content) // wrong: md5 labeled as SHA1

// after
"OC-Checksum: SHA1:" . hash('sha1', $content) // label matches digest, lowercase hex
Defensive patterns

Strategy: validation

Validate before calling

// Compute digest and length from the same final bytes, label correctly
$raw = $part['content'];
$part['headers']['content-length'] = (string)strlen($raw);
$part['headers']['oc-checksum'] = 'SHA1:' . hash('sha1', $raw); // label matches algorithm, lowercase hex

Prevention

When it happens

Trigger: Hash computed over different bytes than sent (length/charset drift between hashing and writing), algorithm label not matching the digest ('OC-Checksum: SHA1:...' holding an MD5), digest cached from an older file version, hex case mismatch (comparison is case-sensitive), or genuine in-transit corruption.

Common situations: Clients caching checksums and reusing them after the file changed; hashing base64-encoded instead of raw bytes; uppercase digests from some tools; proxies mangling binary bodies.

Related errors


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