{"record":{"id":"2e90ea65ab8688b6","repo":"theonedev/onedev","slug":"no-permission-to-add-specified-link-for-specified","errorCode":null,"errorMessage":"No permission to add specified link for specified issues","messagePattern":"No permission to add specified link for specified issues","errorType":"http","errorClass":"UnauthorizedException","httpStatus":403,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/TodResource.java","lineNumber":726,"sourceCode":"    public Map<String, Object> linkIssues(\n                @QueryParam(\"currentProject\") @NotNull String currentProjectPath, \n                @QueryParam(\"sourceReference\") @NotNull String sourceReference, \n                @QueryParam(\"linkName\") @Nullable String linkName, \n                @QueryParam(\"targetReference\") @NotNull String targetReference) {\n        if (SecurityUtils.getUser() == null)\n            throw new UnauthenticatedException();\n\n        var currentProject = getProject(currentProjectPath);\n\n        var sourceIssue = getIssue(currentProject, sourceReference);\n        var targetIssue = getIssue(currentProject, targetReference);\n\n        var linkSpec = linkSpecService.find(linkName);\n        if (linkSpec == null)\n            throw new NotFoundException(\"Link spec not found: \" + linkName);\n        if (!SecurityUtils.canEditIssueLink(sourceIssue.getProject(), linkSpec) \n                || !SecurityUtils.canEditIssueLink(targetIssue.getProject(), linkSpec)) {\n            throw new UnauthorizedException(\"No permission to add specified link for specified issues\");\n        }\n        \n        var link = new IssueLink();\n        link.setSpec(linkSpec);\n        if (linkName.equals(linkSpec.getName())) {\n            link.setSource(sourceIssue);\n            link.setTarget(targetIssue);\n        } else {\n            link.setSource(targetIssue);\n            link.setTarget(sourceIssue);\n        }\n        link.validate();\n        issueLinkService.create(link);\n\n        var linkMap = new HashMap<String, Object>();\n        linkMap.put(\"source\", link.getSource().getReference().toString(currentProject));\n        linkMap.put(\"target\", link.getTarget().getReference().toString(currentProject));\n        linkMap.put(\"linkName\", link.getSpec().getName());","sourceCodeStart":708,"sourceCodeEnd":744,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/TodResource.java#L708-L744","documentation":"Thrown as UnauthorizedException when the authenticated user lacks permission (SecurityUtils.canEditIssueLink) to create the specified link on either the source or the target issue's project. Both projects must permit editing this link type for the user.","triggerScenarios":"Calling the AI tod link-issues endpoint where canEditIssueLink fails for the source issue's project or the target issue's project for the given link spec.","commonSituations":"User can edit links in the source project but the target issue lives in a more restricted project; link spec has restricted authorization; low-privileged user or agent identity.","solutions":["Grant the user permission to edit this issue link type in both projects (project role/permission settings).","Use an identity with link-edit rights on both projects.","Choose a link spec the user is authorized to use across both projects."],"exampleFix":"// before\nuser without 'Edit Issue Link' on target project attempts link\n// after: elevate the user's role in BOTH projects, or authenticate as a user\n// with canEditIssueLink(sourceProject, spec) && canEditIssueLink(targetProject, spec)","handlingStrategy":"try-catch","validationCode":"// check link-edit permission on both projects before linking\nif (!canEditLink(user, sourceIssue.project, linkSpec) ||\n    !canEditLink(user, targetIssue.project, linkSpec)) {\n  throw new Error(\"User lacks link-edit permission on source or target project\");\n}","typeGuard":"function canLinkBoth(user, source, target, spec) {\n  return hasPermission(user, source.project, `link:${spec.name}`)\n      && hasPermission(user, target.project, `link:${spec.name}`);\n}","tryCatchPattern":"try {\n  await linkIssues(...);\n} catch (e) {\n  if (e.status === 401 || /No permission to add specified link/.test(e.message)) {\n    // elevate identity or surface a permissions request to an admin\n  } else throw e;\n}","preventionTips":["Verify link-edit rights on BOTH source and target projects before calling.","Grant AI agent identities consistent link permissions across linked projects.","Prefer link specs whose authorization includes the agent's group."],"tags":["authorization","permissions","issue-links","onedev"],"backgroundTag":"permission-denied","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"}