{"record":{"id":"3d15c51340acf157","repo":"theonedev/onedev","slug":"package-version-param-is-missing","errorCode":null,"errorMessage":"Package version param is missing","messagePattern":"Package version param is missing","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":244,"sourceCode":"\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tURL tplUrl = Resources.getResource(getClass(), \"package-versions.tpl\");\n\t\t\t\t\t\t\t\tString template = Resources.toString(tplUrl, UTF_8);\n\t\t\t\t\t\t\t\tresponse.setContentType(CONTENT_TYPE_SIMPLE);\n\t\t\t\t\t\t\t\tsendResponse(response, evalTemplate(template, bindings));\n\t\t\t\t\t\t\t\tresponse.setStatus(SC_OK);\n\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresponse.setStatus(SC_NOT_FOUND);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (currentSegment.equals(\"files\")) {\n\t\t\t\tif (pathSegments.size() < 1)\n\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Package name param is missing\");\n\t\t\t\telse if (pathSegments.size() < 2)\n\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Package version param is missing\");\n\t\t\t\telse if (pathSegments.size() < 3)\n\t\t\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Package file name param is missing\");\n\t\t\t\t\n\t\t\t\tvar name = UrlUtils.decodePath(pathSegments.get(0));\n\t\t\t\tvar version = UrlUtils.decodePath(pathSegments.get(1));\n\t\t\t\tvar fileName = UrlUtils.decodePath(pathSegments.get(2));\n\t\t\t\t\n\t\t\t\tsessionService.run(() -> {\n\t\t\t\t\tvar project = checkProject(projectId, false);\n\t\t\t\t\tvar pack = packService.findByNameAndVersion(project, TYPE, name, version);\n\t\t\t\t\tif (pack != null) {\n\t\t\t\t\t\tvar data = (PypiData) pack.getData();\n\t\t\t\t\t\tvar sha256BlobHash = data.getSha256BlobHashes().get(fileName);\n\t\t\t\t\t\tif (sha256BlobHash != null) {\n\t\t\t\t\t\t\tPackBlob packBlob;\n\t\t\t\t\t\t\tif ((packBlob = packBlobService.checkPackBlob(projectId, sha256BlobHash)) != null) {\n\t\t\t\t\t\t\t\tresponse.setContentType(MediaType.APPLICATION_OCTET_STREAM);\n\t\t\t\t\t\t\t\ttry {","sourceCodeStart":226,"sourceCodeEnd":262,"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#L226-L262","documentation":"The /~pypi/files download route requires name, version, and file name segments. With only the name supplied (pathSegments.size() < 2 after 'files'), the handler throws ClientException(SC_BAD_REQUEST, \"Package version param is missing\") → HTTP 400.","triggerScenarios":"GET /~pypi/files/<name> with no version and file name segments (PypiPackHandler.java:243-244).","commonSituations":"URL truncation when a link is cut at a slash; template or script interpolating an empty version; manual construction of download URLs.","solutions":["Append both version and file name: /~pypi/files/<name>/<version>/<fileName>","Get the exact link from the package's simple index page","Verify variables feeding URL templates are non-empty","Check for encoders/proxies that drop path segments"],"exampleFix":"// before\nGET /myproj/~pypi/files/mypkg\n// after\nGET /myproj/~pypi/files/mypkg/1.0.0/mypkg-1.0.0-py3-none-any.whl","handlingStrategy":"validation","validationCode":"const segments = filesUrl.split('/').filter(Boolean).slice(-3);\nif (segments.length < 3) throw new Error('Download URL must end with <name>/<version>/<fileName>, got: ' + filesUrl);","typeGuard":null,"tryCatchPattern":"try {\n  const r = await fetch(filesUrl);\n  if (r.status === 400) throw new Error('Bad /files request: ' + await r.text());\n} catch (e) { console.error('Check URL completeness:', e); }","preventionTips":["Never truncate URLs mid-path in logs/scripts","Assert non-empty version before interpolating into URLs","Use index-provided links as the source of truth"],"tags":["http","url","pypi","bad-request"],"backgroundTag":"missing-required-argument","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"}