theonedev/onedev · error · ClientException
Content disposition header not found in uploaded file
Error message
Content disposition header not found in uploaded file
What it means
During the PyPI package upload, the handler reads the 'content-disposition' header of the uploaded multipart file part to derive the file name. If the part has no Content-Disposition header, a ClientException (HTTP 400) is thrown.
Source
Thrown at server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java:133
var version = getAttribute(attributes, "version");
var sha256Hash = getAttribute(attributes, "sha256_digest");
attributes.remove("name");
attributes.remove("version");
attributes.remove("filetype");
attributes.remove("metadata_version");
attributes.remove("pyversion");
attributes.remove("sha256_digest");
attributes.remove("md5_digest");
attributes.remove("blake2_256_digest");
attributes.remove(":action");
attributes.remove("protocol_version");
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) {View on GitHub (pinned to d44925c47c)
Solutions
- Send the upload as proper multipart/form-data so each part carries a Content-Disposition header (curl -F, requests' files=).
- If building the request manually, add 'Content-Disposition: form-data; name="file"; filename="pkg.whl"' to the file part.
- Capture the outgoing request (proxy/log) and confirm the file part includes the content-disposition header.
Example fix
// before: raw body post curl --data-binary @pkg.whl http://server/~pypi/upload // after: multipart with filename curl -F "file=@pkg.whl" http://server/~pypi/upload
Defensive patterns
Strategy: validation
Validate before calling
# verify the request is multipart/form-data with a file part
files = {'file': ('pkg.whl', open('pkg.whl','rb'), 'application/octet-stream')}
requests.post(url, files=files, ...) Try / catch
if resp.status_code == 400 and 'content disposition' in resp.text:
raise RuntimeError('Send upload as multipart/form-data, not raw body') Prevention
- Always use multipart upload APIs (curl -F, requests files=) for this endpoint.
- Never post the package as a raw binary body.
- Test upload scripts against a dev instance first.
When it happens
Trigger: Uploading the package file as a raw (non-multipart) body, or constructing the multipart request manually without a Content-Disposition header on the file part.
Common situations: Hand-rolled curl/HTTP-client scripts that send the file without proper multipart/form-data framing; a proxy stripping headers; using a client library that posts the file as octet-stream instead of a multipart part.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Attribute not found: ${attributeKey}
- File name not found in content disposition header of uploade
- Digest mismatch
- This method is only meant to be invoked on the component whe
- DIGEST_INVALID
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/c66e0a39f429c4ea.
Report an issue: GitHub.