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

  1. Send the upload as proper multipart/form-data so each part carries a Content-Disposition header (curl -F, requests' files=).
  2. If building the request manually, add 'Content-Disposition: form-data; name="file"; filename="pkg.whl"' to the file part.
  3. 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

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


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/c66e0a39f429c4ea. Report an issue: GitHub.