{"record":{"id":"5bc943b2eac14f5b","repo":"theonedev/onedev","slug":"405-5bc943","errorCode":null,"errorMessage":"405","messagePattern":"405","errorType":"http","errorClass":"ClientException","httpStatus":405,"severity":"error","filePath":"server-plugin/server-plugin-pack-pypi/src/main/java/io/onedev/server/plugin/pack/pypi/PypiPackHandler.java","lineNumber":191,"sourceCode":"\t\t\t\t\t\t\t\t\t} \n\t\t\t\t\t\t\t\t\tdata.getSha256BlobHashes().put(fileName, sha256Hash);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tvar packBlobs = data.getSha256BlobHashes().values().stream()\n\t\t\t\t\t\t\t\t\t\t\t.map(hash -> packBlobService.findBySha256Hash(projectId, hash))\n\t\t\t\t\t\t\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t\t\t\t\t\t\t.collect(toList());\n\t\t\t\t\t\t\t\t\tpackService.createOrUpdate(pack, packBlobs, data.getSha256BlobHashes().size() == 1);\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\tresponse.setStatus(SC_OK);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException | FileUploadException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new ClientException(SC_METHOD_NOT_ALLOWED);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!isGet)\n\t\t\t\tthrow new ClientException(SC_METHOD_NOT_ALLOWED);\n\t\t\tvar currentSegment = pathSegments.get(0);\n\t\t\tpathSegments = pathSegments.subList(1, pathSegments.size());\n\t\t\t\n\t\t\t// https://peps.python.org/pep-0503/\n\t\t\tif (currentSegment.equals(\"simple\")) { \n\t\t\t\tif (pathSegments.isEmpty()) {\n\t\t\t\t\tsessionService.run(() -> {\n\t\t\t\t\t\tvar project = checkProject(projectId, false);\n\t\t\t\t\t\tvar names = packService.queryNames(project, TYPE, null, true, 0, MAX_VALUE);\n\t\t\t\t\t\tvar bindings = new HashMap<String, Object>();\n\t\t\t\t\t\tbindings.put(\"names\", names);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tURL tplUrl = Resources.getResource(getClass(), \"packages.tpl\");\n\t\t\t\t\t\t\tString template = Resources.toString(tplUrl, UTF_8);","sourceCodeStart":173,"sourceCodeEnd":209,"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#L173-L209","documentation":"The PyPI pack handler received a non-POST request on the upload endpoint. Only POST (multipart upload) is allowed there; any other method throws ClientException(SC_METHOD_NOT_ALLOWED), which the client sees as HTTP 405.","triggerScenarios":"Issuing GET/PUT/DELETE against the project's ~pypi upload URL while the request is not a GET handled by the simple/index branch — i.e. wrong HTTP verb used for the upload route (PypiPackHandler.java:190-191).","commonSituations":"curl-ing the upload URL to 'test' it with GET; a misconfigured CI script using PUT instead of twine's POST; a health-check probe hitting the upload endpoint with HEAD/GET.","solutions":["Use POST with multipart/form-data (or the standard twine upload command) against the upload endpoint","Switch the request method to POST in the script or curl command (curl -X POST --form ...)","Point read-only checks at the /simple index endpoint instead, which accepts GET","Check any reverse-proxy rewrite rules that might change the HTTP method"],"exampleFix":"// before\ncurl -X PUT https://onedev.example.com/myproj/~pypi/upload -F file=mypkg.whl\n// after\ncurl -X POST https://onedev.example.com/myproj/~pypi/upload -F \"content=@mypkg.whl\"","handlingStrategy":"validation","validationCode":"// ensure the upload request uses POST\nif (httpRequest.method !== 'POST') {\n  throw new Error('PyPI upload endpoint requires POST, got ' + httpRequest.method);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await upload(url, formData);\n} catch (e) {\n  if (e.status === 405) {\n    console.error('Wrong HTTP method for the PyPI endpoint; use POST for upload, GET for /simple', e);\n  } else { throw e; }\n}","preventionTips":["Use twine for publishing rather than hand-rolled HTTP calls","Keep upload and index URLs distinct in scripts","Automate method checks in CI before publishing"],"tags":["http","method-not-allowed","pypi"],"backgroundTag":"http-error-status","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}