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
- Recompute the hash immediately before sending, over the exact bytes of the part body, with the algorithm matching the header label
- Use lowercase hex (PHP hash()/hash_file() output) - the server compares raw strings case-sensitively
- 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
- Never reuse cached digests after a file changes - rehash before every upload
- Use lowercase hex digests; the comparison is case-sensitive ===
- Keep the algorithm prefix of OC-Checksum consistent with how the digest was computed
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
- The hash headers must not be null.
- No hash provided.
- An error occurred while checking content
- Boundary not found where it should be.
- An error occurred while reading headers of a part
AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17).
Data as JSON: /api/errors/df4e7505142f077a.
Report an issue: GitHub.