{"record":{"id":"0ee278274b96b17f","repo":"theonedev/onedev","slug":"attachment-parameter-has-to-be-specified","errorCode":null,"errorMessage":"attachment parameter has to be specified","messagePattern":"attachment parameter has to be specified","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java","lineNumber":96,"sourceCode":"\t\t\t\tif (OneDev.getInstance(PullRequestService.class).find(attachmentGroup) != null\n\t\t\t\t\t\t|| OneDev.getInstance(CodeCommentService.class).findByUUID(attachmentGroup) != null) {\n\t\t\t\t\tif (!SecurityUtils.canReadCode(project))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if ((issue = OneDev.getInstance(IssueService.class).find(attachmentGroup)) != null) {\n\t\t\t\t\tif (!SecurityUtils.canAccessIssue(issue))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if ((build = OneDev.getInstance(BuildService.class).find(attachmentGroup)) != null) {\n\t\t\t\t\tif (!SecurityUtils.canAccessProject(build.getProject()))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if (!SecurityUtils.canAccessProject(project)) {\n\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tString attachment = params.get(PARAM_ATTACHMENT).toString();\n\t\tif (StringUtils.isBlank(attachment))\n\t\t\tthrow new IllegalArgumentException(\"attachment parameter has to be specified\");\n\t\telse if (attachment.contains(\"..\"))\n\t\t\tthrow new IllegalArgumentException(\"Invalid attachment parameter\");\n\n\t\tResourceResponse response = new ResourceResponse();\n\t\tresponse.setContentLength(getAttachmentService().getAttachmentInfo(projectId, attachmentGroup, attachment).getLength());\n\t\t\n\t\tresponse.getHeaders().addHeader(\"X-Content-Type-Options\", \"nosniff\");\n\t\tresponse.setContentType(MimeTypes.OCTET_STREAM);\n\n\t\tresponse.setFileName(URLEncoder.encode(attachment, UTF_8));\n\n\t\tresponse.setWriteCallback(new WriteCallback() {\n\n\t\t\t@Override\n\t\t\tpublic void writeData(Attributes attributes) throws IOException {\n\t\t\t\tString activeServer = getProjectService().getActiveServer(projectId, true);\n\t\t\t\tClusterService clusterService = OneDev.getInstance(ClusterService.class);\n\t\t\t\tif (activeServer.equals(clusterService.getLocalServerAddress())) {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java#L78-L114","documentation":"AttachmentResource requires the 'attachment' query/path parameter naming the file to download. If the parameter is absent or blank, newResourceResponse throws IllegalArgumentException(\"attachment parameter has to be specified\"), which the resource layer reports to the client as a 400-style error.","triggerScenarios":"GET /~resource/attachments/<projectId>/<group> without the PARAM_ATTACHMENT query parameter, or with an empty value (params.get(PARAM_ATTACHMENT).toString() is blank).","commonSituations":"Hand-built URLs missing the ?attachment= part; template/URL-generation bug dropping the query string; scripts stripping parameters containing special characters without encoding.","solutions":["Append the attachment file name as the query parameter, e.g. ?attachment=report.pdf (URL-encode special characters).","Fix the code or template that builds the URL so it always includes the attachment parameter.","Verify the exact parameter name against AttachmentResource.PARAM_ATTACHMENT in the OneDev version in use."],"exampleFix":"// before\nGET /~resource/attachments/1/issue-42          -> 400\n// after\nGET /~resource/attachments/1/issue-42?attachment=trace.log","handlingStrategy":"validation","validationCode":"if (!attachmentName || attachmentName.trim() === '') {\n  throw new Error('attachment query parameter is required, e.g. ?attachment=report.pdf');\n}","typeGuard":"function hasAttachment(params) {\n  return typeof params.attachment === 'string' && params.attachment.length > 0;\n}","tryCatchPattern":"try { const res = await fetch(url); } catch (e) { if (String(e.message).includes('attachment parameter')) console.error('URL must include ?attachment=<name>'); else throw e; }","preventionTips":["Always build attachment URLs via a helper that appends ?attachment=","URL-encode file names","Validate URLs in scripts before issuing requests"],"tags":["onedev","http","bad-request","attachments"],"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"}