{"record":{"id":"d41cab2119474f0b","repo":"theonedev/onedev","slug":"attribute-not-found-attributekey","errorCode":null,"errorMessage":"Attribute not found: ${attributeKey}","messagePattern":"Attribute not found: (.+?)","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":88,"sourceCode":"\t\tthis.packBlobService = packBlobService;\n\t\tthis.packService = packService;\n\t\tthis.projectService = projectService;\n\t\tthis.buildService = buildService;\n\t}\n\t\n\t@Override\n\tpublic String getHandlerId() {\n\t\treturn HANDLER_ID;\n\t}\n\n\tprivate String getLockName(Long projectId, String name) {\n\t\treturn \"update-pack:\" + projectId + \":\" + TYPE + \":\" + name;\n\t}\n\t\n\tprivate String getAttribute(Map<String, List<String>> attributes, String attributeKey) {\n\t\tvar value = attributes.get(attributeKey);\n\t\tif (value == null || value.isEmpty())\n\t\t\tthrow new ClientException(SC_BAD_REQUEST, \"Attribute not found: \" + attributeKey);\n\t\treturn value.get(0);\n\t}\n\t\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response, Long projectId, \n\t\t\t\t\t\tLong buildId, List<String> pathSegments) {\n\t\tvar method = request.getMethod();\n\t\t\n\t\tvar isGet = method.equals(\"GET\");\n\t\tvar isPost = method.equals(\"POST\");\n\t\t\n\t\tif (pathSegments.isEmpty()) {\n\t\t\tif (isPost) {\n\t\t\t\tvar upload = new ServletFileUpload();\n\t\t\t\ttry {\n\t\t\t\t\tvar attributes = new LinkedHashMap<String, List<String>>();\n\t\t\t\t\tvar items = upload.getItemIterator(request);\n\t\t\t\t\twhile (items.hasNext()) {","sourceCodeStart":70,"sourceCodeEnd":106,"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#L70-L106","documentation":"getAttribute() throws a ClientException (HTTP 400) when a required metadata attribute is missing or empty in the multipart upload to the PyPI pack endpoint. Helpers name(), version(), and sha256Hash() call it for 'name', 'version', and 'sha256' attributes respectively.","triggerScenarios":"POSTing a package upload where the multipart form is missing the 'name', 'version', or 'sha256' field, or sends it with an empty value; sending fields under different names than the handler expects.","commonSituations":"A custom CI publish script (curl/wget) that omits required form fields; a misconfigured upload pipeline; using an outdated upload script with old field names after an upgrade.","solutions":["Include all required multipart fields in the upload: name, version, sha256 (as form attributes alongside the file).","Verify each field is non-empty and spelled exactly as expected (lowercase keys).","If using a custom script, print the request fields before sending and compare with the handler's expected attributes."],"exampleFix":"// before\ncurl -F \"file=@pkg.whl\" http://server/~pypi/...\n// after\ncurl -F \"name=mypkg\" -F \"version=1.0.0\" -F \"sha256=<digest>\" -F \"file=@pkg.whl\" http://server/~pypi/...","handlingStrategy":"validation","validationCode":"required = {'name': name, 'version': version, 'sha256': sha256}\nmissing = [k for k, v in required.items() if not v]\nassert not missing, f'missing upload attributes: {missing}'","typeGuard":null,"tryCatchPattern":"if resp.status_code == 400 and 'Attribute not found' in resp.text:\n    fix_missing_fields(resp.text.split(':')[-1].strip())","preventionTips":["Keep the upload field names in sync with the handler (name, version, sha256).","Validate form data client-side before sending.","Review upload scripts when upgrading OneDev."],"tags":["pypi","http-400","multipart","validation"],"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"}