theonedev/onedev · error · RuntimeException
internal error
Error message
internal error
What it means
During a PyPI package upload (POST multipart) handled by PypiPackHandler.handle, an IOException or FileUploadException occurred while parsing the multipart form upload. The handler wraps it in a plain RuntimeException, which OneDev surfaces to the client as a generic 'internal error'. It indicates the upload request body could not be read or parsed as multipart.
Source
Thrown at server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java:188
if (data.getSha256BlobHashes().containsKey(fileName)) {
var errorMessage = String.format("Package already exists (name: %s, version: %s)", name, version);
throw new ClientException(SC_CONFLICT, errorMessage);
}
data.getSha256BlobHashes().put(fileName, sha256Hash);
var packBlobs = data.getSha256BlobHashes().values().stream()
.map(hash -> packBlobService.findBySha256Hash(projectId, hash))
.filter(Objects::nonNull)
.collect(toList());
packService.createOrUpdate(pack, packBlobs, data.getSha256BlobHashes().size() == 1);
}));
response.setStatus(SC_OK);
break;
}
}
}
} catch (IOException | FileUploadException e) {
throw new RuntimeException(e);
}
} else {
throw new ClientException(SC_METHOD_NOT_ALLOWED);
}
} else {
if (!isGet)
throw new ClientException(SC_METHOD_NOT_ALLOWED);
var currentSegment = pathSegments.get(0);
pathSegments = pathSegments.subList(1, pathSegments.size());
// https://peps.python.org/pep-0503/
if (currentSegment.equals("simple")) {
if (pathSegments.isEmpty()) {
sessionService.run(() -> {
var project = checkProject(projectId, false);
var names = packService.queryNames(project, TYPE, null, true, 0, MAX_VALUE);
var bindings = new HashMap<String, Object>();
bindings.put("names", names);View on GitHub (pinned to d44925c47c)
Solutions
- Check server logs for the root IOException/FileUploadException stack trace below the 'internal error' to identify the real cause
- Retry the twine upload and verify the multipart Content-Type and boundary are preserved end-to-end
- Inspect proxy/load-balancer settings for request body size limits and timeouts that could truncate the upload
- Verify sufficient disk space and writable temp directory on the OneDev server for multipart spooling
- Upgrade the client (twine) if it sends a non-standard multipart encoding
Defensive patterns
Strategy: try-catch
Validate before calling
// client side: verify multipart body before upload
const contentType = respHeaders['content-type'] || '';
if (!contentType.includes('multipart/form-data')) throw new Error('Upload must be multipart/form-data');
if (!fs.existsSync(pkgFile)) throw new Error('Package file not found: ' + pkgFile); Try / catch
try {
await twine.upload(repoUrl, pkgFile);
} catch (e) {
if (e.response && /internal error/.test(e.message)) {
console.error('Upload failed server-side; check OneDev server logs for wrapped IOException/FileUploadException', e);
} else { throw e; }
} Prevention
- Always upload with twine or a client that produces standard multipart/form-data
- Check proxy body-size limits and timeouts before pushing large wheels
- Monitor server disk space for multipart spooling
- Retry transient network failures with backoff
When it happens
Trigger: POSTing a package file to the ~pypi upload endpoint with a malformed multipart body, a truncated/interrupted upload, missing or malformed content-disposition part, or an invalid multipart boundary; the catch block at PypiPackHandler.java:187-188 converts the underlying IOException/FileUploadException into RuntimeException.
Common situations: twine upload interrupted by network drop; a proxy (nginx, corporate proxy) truncating or buffering the request body; client sending a non-multipart body or wrong Content-Type header; disk I/O problems on the server while spooling the upload.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Chart archive not found
- Error reading chart archive: ${e.getMessage()}
- Unauthenticated
- Upload must be less than
- Upload project not found:
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/9b80af4575196bbe.
Report an issue: GitHub.