theonedev/onedev · error · ClientException
Digest mismatch
Error message
Digest mismatch
What it means
After uploading the file bytes, packBlobService.uploadBlob(projectId, is, sha256Hash) returns null when the SHA-256 digest of the received content does not match the sha256 attribute supplied by the client. The handler then throws a ClientException (HTTP 400) 'Digest mismatch'.
Source
Thrown at server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java:147
LockUtils.run(getLockName(projectId, name), () -> transactionService.run(() -> {
var project = checkProject(projectId, true);
var contentDisposition = item.getHeaders().getHeader("content-disposition");
if (contentDisposition == null)
throw new ClientException(SC_BAD_REQUEST, "Content disposition header not found in uploaded file");
String fileName = null;
for (var field: Splitter.on(";").omitEmptyStrings().trimResults().split(contentDisposition)) {
if (field.startsWith("filename=")) {
fileName = field.substring("filename=".length() + 1);
fileName = fileName.substring(0, fileName.length() - 1);
break;
}
}
if (fileName == null)
throw new ClientException(SC_BAD_REQUEST, "File name not found in content disposition header of uploaded file");
var packBlobId = packBlobService.uploadBlob(projectId, is, sha256Hash);
if (packBlobId == null)
throw new ClientException(SC_BAD_REQUEST, "Digest mismatch");
PypiData data;
var pack = packService.findByNameAndVersion(project, TYPE, name, version);
if (pack == null) {
pack = new Pack();
pack.setType(TYPE);
pack.setName(name);
pack.setVersion(version);
pack.setProject(project);
data = new PypiData(attributes, new LinkedHashMap<>());
pack.setData(data);
} else {
data = (PypiData) pack.getData();
}
Build build = null;
if (buildId != null)
build = buildService.load(buildId);View on GitHub (pinned to d44925c47c)
Solutions
- Recompute sha256 on the exact file being uploaded (e.g. sha256sum pkg.whl) and resend with the corrected value.
- Ensure the upload transfers bytes unchanged (binary mode; no proxy body transformation).
- Make sure the file attached to the request is the same artifact the hash was computed from (same build output).
Example fix
// before -H "sha256: $(sha256sum old.whl | cut -d' ' -f1)" -F "file=@new.whl" // after SHA=$(sha256sum new.whl | cut -d' ' -f1) curl -F "sha256=$SHA" -F "file=@new.whl" http://server/~pypi/upload
Defensive patterns
Strategy: validation
Validate before calling
import hashlib
expected = hashlib.sha256(open('pkg.whl','rb').read()).hexdigest()
assert expected == declared_sha256, 'hash computed on the wrong artifact' Try / catch
if resp.status_code == 400 and 'Digest mismatch' in resp.text:
recompute_and_resend() Prevention
- Compute the hash on the exact file attached to the request, immediately before upload.
- Upload files in binary mode; disable proxies that rewrite bodies.
- Generate the hash and upload in the same pipeline step.
When it happens
Trigger: Publishing a PyPI package where the declared sha256 form field does not equal the actual SHA-256 of the uploaded file bytes (recomputed server-side).
Common situations: Computing the hash before a tool re-writes/re-compresses the file; sending the digest of a different file than the one attached; text-mode corruption of the file during transfer; stale hash from an earlier build artifact.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Integrity check failed:
- Invalid blob size: %s
- Checksum verification failed
- Attribute not found: ${attributeKey}
- Content disposition header not found in uploaded file
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/6a26de4616d5bff0.
Report an issue: GitHub.