{"record":{"id":"cc66295c69fc6924","repo":"theonedev/onedev","slug":"pull-request-is-closed","errorCode":null,"errorMessage":"Pull request is closed","messagePattern":"Pull request is closed","errorType":"http","errorClass":"NotAcceptableException","httpStatus":406,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/TodResource.java","lineNumber":1355,"sourceCode":"        if (title != null) \n            pullRequestChangeService.changeTitle(user, request, title);\n\n        if (data.containsKey(\"description\")) \n            pullRequestChangeService.changeDescription(user, request, (String) data.remove(\"description\"));\n\n        var labelNames = (List<String>) data.remove(\"labels\");\n        if (labelNames != null) {\n            try {\n                pullRequestLabelService.sync(request, labelNames);\n            } catch (EntityNotFoundException e) {\n                throw new NotFoundException(e.getMessage());\n            }\n        }\n\n        var mergeStrategyName = (String) data.remove(\"mergeStrategy\");\n        if (mergeStrategyName != null) {\n            if (!request.isOpen())\n                throw new NotAcceptableException(\"Pull request is closed\");\n            pullRequestChangeService.changeMergeStrategy(user, request, MergeStrategy.valueOf(mergeStrategyName));\n        }\n\n        var assigneeNames = (List<String>) data.remove(\"assignees\");\n        if (assigneeNames != null) {                        \n            if (!request.isOpen())\n                throw new NotAcceptableException(\"Pull request is closed\");\n            for (var assigneeName : assigneeNames) {\n                User assignee = userService.findByName(assigneeName);\n                if (assignee == null)\n                    throw new NotFoundException(\"Assignee not found: \" + assigneeName);\n                if (request.getAssignments().stream().noneMatch(it -> it.getUser().equals(assignee))) {\n                    PullRequestAssignment assignment = new PullRequestAssignment();\n                    assignment.setRequest(request);\n                    assignment.setUser(assignee);\n                    pullRequestAssignmentService.create(assignment);\n                }\n            }","sourceCodeStart":1337,"sourceCodeEnd":1373,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/TodResource.java#L1337-L1373","documentation":"The endpoint refuses to change the merge strategy of a pull request that is no longer open (merged or discarded). request.isOpen() is checked before pullRequestChangeService.changeMergeStrategy() runs, and a NotAcceptableException is thrown when the check fails.","triggerScenarios":"POST /edit-pull-request with 'mergeStrategy' set on a pull request whose state is MERGED or DISCARDED.","commonSituations":"An automation or AI agent fetched the PR details, another user merged/closed it, then the agent submitted the mergeStrategy update; racing workflows that edit stale PR references.","solutions":["Re-fetch the pull request and confirm it is open before sending 'mergeStrategy'.","If the PR is already merged, the merge strategy is moot — drop the field from the request.","If the PR was discarded, reopen it (if possible) before changing the merge strategy.","Make editing workflows optimistic: handle 406/NotAcceptable responses by refreshing state and retrying only relevant fields."],"exampleFix":"// before\n// blindly POST mergeStrategy after long processing\n// after\nif (pullRequest.state === 'open') {\n  await editPullRequest({ mergeStrategy: 'SQUASH' });\n} else {\n  // skip strategy change; PR closed\n}","handlingStrategy":"validation","validationCode":"const pr = await getPullRequest(ref);\nif (pr.state !== 'open') throw new Error(`PR ${ref} is ${pr.state}; mergeStrategy cannot be changed`);","typeGuard":"function isEditableOpen(pr) { return pr && pr.state === 'open'; }","tryCatchPattern":"try {\n  await editPullRequest(ref, { mergeStrategy: 'SQUASH' });\n} catch (e) {\n  if (e.status === 406 && e.message.includes('closed')) {\n    await refreshPullRequest(ref); // re-sync state and skip\n  }\n}","preventionTips":["Re-fetch PR state immediately before editing; never edit from stale data.","Skip closed PRs in batch jobs.","Keep the window between read and write short to avoid races.","Handle 406 responses as expected control flow, not crashes."],"tags":["rest","pull-request","state","onedev"],"backgroundTag":"invalid-state-transition","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"}