{"record":{"id":"8a3666637cba6a5f","repo":"theonedev/onedev","slug":"reviewer-needs-to-have-read-code-permission-to-the","errorCode":null,"errorMessage":"Reviewer needs to have read code permission to the project","messagePattern":"Reviewer needs to have read code permission to the project","errorType":"http","errorClass":"NotAcceptableException","httpStatus":406,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/PullRequestResource.java","lineNumber":361,"sourceCode":"\t\treturn Response.ok().build();\n    }\n\n\t@Api(order=1410)\n\t@Path(\"/{requestId}/reviewers/{userId}\")\n\t@POST\n\tpublic Response addReviewer(@PathParam(\"requestId\") Long requestId, @PathParam(\"userId\") Long userId) {\t\t\n\t\tvar request = pullRequestService.load(requestId);\n\t\tvar user = userService.load(userId);\n\n\t\tvar currentUser = SecurityUtils.getUser();\n\t\tif (!SecurityUtils.canModifyPullRequest(currentUser.asSubject(), request)) \n\t\t\tthrow new UnauthorizedException();\n\n\t\tif (user.equals(request.getSubmitter()))\n\t\t\tthrow new NotAcceptableException(\"Pull request submitter cannot be reviewer\");\n\n\t\tif (!SecurityUtils.canReadCode(user.asSubject(), request.getProject()))\n\t\t\tthrow new NotAcceptableException(\"Reviewer needs to have read code permission to the project\");\n\t\t\t\n\t\tvar review = request.getReview(user);\n\t\tif (review != null) {\n\t\t\tif (review.getStatus() == EXCLUDED) {\n\t\t\t\treview.setStatus(PENDING);\n\t\t\t\tpullRequestReviewService.createOrUpdate(currentUser, review);\n\t\t\t}\n\t\t} else {\n\t\t\treview = new PullRequestReview();\n\t\t\treview.setRequest(request);\n\t\t\treview.setUser(user);\n\t\t\treview.setStatus(PENDING);\n\n\t\t\tpullRequestReviewService.createOrUpdate(currentUser, review);\t\n\t\t}\n\n\t\treturn Response.ok().build();\n\t}","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/PullRequestResource.java#L343-L379","documentation":"OneDev throws this NotAcceptableException (HTTP 400) from the REST endpoint POST /pull-requests/{requestId}/reviewers/{userId} when the caller tries to add a reviewer who lacks read-code permission on the pull request's target project. A reviewer must be able to see the code to review it, so the API refuses users outside the project's reader ACL. It is a validation of the target user's permissions, not the caller's.","triggerScenarios":"Calling addReviewer (POST .../reviewers/{userId}) where SecurityUtils.canReadCode(user.asSubject(), request.getProject()) returns false — i.e. the nominated user has no Reader role or higher on the target project (e.g. a logged-in user with no project membership, or a user of another project).","commonSituations":"Admin adds a reviewer from a different project/team who was never granted access; project ACL was tightened and previously-addable users became ineligible; automation scripts pick arbitrary user IDs; a guest-level account is nominated.","solutions":["Grant the reviewer at least 'Read Code' (Reader) permission on the target project, then retry the call.","Pick a different userId that already has read access to the project.","If project roles are the issue, add the user to a group with project read access and verify via the project's access configuration."],"exampleFix":"// before: add arbitrary user as reviewer\nPOST /~api/pull-requests/42/reviewers/107  -> 400 NotAcceptableException\n// after: grant user 107 Reader role on project first (Admin > Project > Access Management), then\nPOST /~api/pull-requests/42/reviewers/107  -> 200 OK","handlingStrategy":"validation","validationCode":"// Check the candidate reviewer has read access before calling the API\nconst canRead = await onedevApi.projectQueryPermission(projectId, \"READ_CODE\", reviewerUserId);\nif (!canRead || reviewerUserId === pr.submitter.id) {\n  throw new Error(`User ${reviewerUserId} cannot review project ${projectId}: grant READ_CODE first`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.addReviewer(requestId, userId);\n} catch (e) {\n  if (e.status === 400 && /read code permission/.test(e.body)) {\n    // grant read permission or choose another reviewer\n  } else throw e;\n}","preventionTips":["Only nominate users from teams with at least Reader access on the target project","Never nominate the PR submitter as reviewer","Synchronize project ACL with your reviewer picker UI","Catch HTTP 400 and surface a clear message listing the permission needed"],"tags":["rest","permissions","pull-request","code-review"],"backgroundTag":"insufficient-permissions","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"}