{"record":{"id":"7f6fbd497e3a217b","repo":"theonedev/onedev","slug":"pull-request-submitter-cannot-be-reviewer-7f6fbd","errorCode":null,"errorMessage":"Pull request submitter cannot be reviewer","messagePattern":"Pull request submitter cannot be reviewer","errorType":"http","errorClass":"NotAcceptableException","httpStatus":406,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/PullRequestResource.java","lineNumber":358,"sourceCode":"    \tif (!SecurityUtils.canModifyPullRequest(subject, request))\n\t\t\tthrow new UnauthorizedException();\n\t\tpullRequestChangeService.changeDescription(user, request, description);\n\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}","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/PullRequestResource.java#L340-L376","documentation":"addReviewer rejects with NotAcceptableException 'Pull request submitter cannot be reviewer' when the userId being added is the pull request's own submitter. OneDev enforces this domain rule because a submitter reviewing their own changes is meaningless for the review workflow; the same rule exists in the REST create-PR path.","triggerScenarios":"PUT /~api/pull-requests/{requestId}/reviewers/{userId} where userId equals request.submitter.id; scripts that build reviewer lists from participants without excluding the author; auto-assignment plugins not filtering the submitter.","commonSituations":"CODEOWNERS-like automation adding every mentioned user; bulk tools copying reviewers between PRs by the same author; tests using the same account for both submit and review.","solutions":["Exclude the submitter's id from the reviewer list before calling.","Filter participant-derived lists against request.submitterId.","Add a guard in your automation: skip a user if user.id == request.submitter.id.","Pick a different reviewer from the team."],"exampleFix":"// before\ncurl -X PUT .../pull-requests/50/reviewers/7   // user 7 submitted PR 50 -> 406\n// after\nif (userId !== request.submitterId) {\n  curl -X PUT .../pull-requests/50/reviewers/${userId}\n}","handlingStrategy":"validation","validationCode":"function eligibleReviewer(user, pullRequest) {\n  return user.id !== pullRequest.submitterId && user.active && user.projectIds.includes(pullRequest.projectId)\n}\n// filter before PUT /reviewers/{userId}\nif (!eligibleReviewer(candidate, pr)) return","typeGuard":null,"tryCatchPattern":"try {\n  await api.put(`/pull-requests/${id}/reviewers/${userId}`)\n} catch (e) {\n  if (e.status === 400 && e.message.includes('submitter cannot be reviewer')) return // already-handled rule\n  throw e\n}","preventionTips":["Always exclude the submitter from generated reviewer lists.","When copying reviewers across PRs, re-filter against each PR's submitter.","Encode the rule in shared client helpers used by all tooling.","Treat 400 'submitter cannot be reviewer' as an expected no-op in bulk scripts."],"tags":["rest","validation","reviewers","onedev"],"backgroundTag":"invalid-argument-value","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"}