{"record":{"id":"6a26de4616d5bff0","repo":"theonedev/onedev","slug":"digest-mismatch","errorCode":null,"errorMessage":"Digest mismatch","messagePattern":"Digest mismatch","errorType":"http","errorClass":"ClientException","httpStatus":400,"severity":"error","filePath":"server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java","lineNumber":147,"sourceCode":"\t\t\t\t\t\t\t\tLockUtils.run(getLockName(projectId, name), () -> transactionService.run(() -> {\n\t\t\t\t\t\t\t\t\tvar project = checkProject(projectId, true);\n\t\t\t\t\t\t\t\t\tvar contentDisposition = item.getHeaders().getHeader(\"content-disposition\"); \n\t\t\t\t\t\t\t\t\tif (contentDisposition == null)\n\t\t\t\t\t\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Content disposition header not found in uploaded file\");\n\t\t\t\t\t\t\t\t\tString fileName = null;\n\t\t\t\t\t\t\t\t\tfor (var field: Splitter.on(\";\").omitEmptyStrings().trimResults().split(contentDisposition)) {\n\t\t\t\t\t\t\t\t\t\tif (field.startsWith(\"filename=\")) {\n\t\t\t\t\t\t\t\t\t\t\tfileName = field.substring(\"filename=\".length() + 1);\n\t\t\t\t\t\t\t\t\t\t\tfileName = fileName.substring(0, fileName.length() - 1);\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (fileName == null) \n\t\t\t\t\t\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"File name not found in content disposition header of uploaded file\");\n\n\t\t\t\t\t\t\t\t\tvar packBlobId = packBlobService.uploadBlob(projectId, is, sha256Hash);\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tif (packBlobId == null)\n\t\t\t\t\t\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Digest mismatch\");\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tPypiData data;\n\t\t\t\t\t\t\t\t\tvar pack = packService.findByNameAndVersion(project, TYPE, name, version);\n\t\t\t\t\t\t\t\t\tif (pack == null) {\n\t\t\t\t\t\t\t\t\t\tpack = new Pack();\n\t\t\t\t\t\t\t\t\t\tpack.setType(TYPE);\n\t\t\t\t\t\t\t\t\t\tpack.setName(name);\n\t\t\t\t\t\t\t\t\t\tpack.setVersion(version);\n\t\t\t\t\t\t\t\t\t\tpack.setProject(project);\n\t\t\t\t\t\t\t\t\t\tdata = new PypiData(attributes, new LinkedHashMap<>());\n\t\t\t\t\t\t\t\t\t\tpack.setData(data);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tdata = (PypiData) pack.getData();\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tBuild build = null;\n\t\t\t\t\t\t\t\t\tif (buildId != null)\n\t\t\t\t\t\t\t\t\t\tbuild = buildService.load(buildId);","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java#L129-L165","documentation":"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'.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\n-H \"sha256: $(sha256sum old.whl | cut -d' ' -f1)\" -F \"file=@new.whl\"\n// after\nSHA=$(sha256sum new.whl | cut -d' ' -f1)\ncurl -F \"sha256=$SHA\" -F \"file=@new.whl\" http://server/~pypi/upload","handlingStrategy":"validation","validationCode":"import hashlib\nexpected = hashlib.sha256(open('pkg.whl','rb').read()).hexdigest()\nassert expected == declared_sha256, 'hash computed on the wrong artifact'","typeGuard":null,"tryCatchPattern":"if resp.status_code == 400 and 'Digest mismatch' in resp.text:\n    recompute_and_resend()","preventionTips":["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."],"tags":["pypi","http-400","checksum","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}